grim/convey

Rename inject task to environment.
inject
2017-10-13, Eric Fritz
f37bad7a9b83
Parents ad2092e0f404
Children f52cce4913a6
Rename inject task to environment.
--- a/REFERENCE.md Wed Oct 11 20:48:50 2017 -0500
+++ b/REFERENCE.md Fri Oct 13 12:04:21 2017 -0500
@@ -50,9 +50,9 @@
This example shows how to expand an environment variable into a list in the context of a extended task.
-## inject.yml
+## environment-from-file.yml
-This example shows how to inject variables from a file in the build container into the plan.
+This example shows how to inject variables into the environment from a file in the build container.
## login-logout.yml
@@ -176,14 +176,14 @@
----
-### Inject task
+### Environment task
-An inject task will read a variable with environment variables and make them accessible to your plans.
+An environment task will read a file with environment variables and make them accessible to your plans.
A file is expected to have lines of the form `variable=value`. Empty lines are ignored. Files are read
from the docker volume, so files generated by the plan do not need to be exported in order to be a target
-of an inject task. From the other direction, it is necessary to import files on the host if they are to be
-read by an inject task.
+of an environment task. From the other direction, it is necessary to import files on the host if they are
+to be read by an environment task.
#### Attributes
@@ -193,17 +193,16 @@
| files | | | A list of files that should be read. |
| prefix | | | A prefix to add to each variable read from a file |
-At least one file must be supplied by either the `file` or `files` attributes. If both are
-supplied, `file` is inserted to the front of `files`. If the files being read are a result of
-environment variable expansion, the order that the files are read are not guaranteed to be
-stable (or in the order supplied). Be cautious of this if the environment files have define
-overlapping variables.
+At least one file must be supplied by either the `from-file` or `fromfiles` attributes. If both are supplied,
+`from-file` is inserted to the front of `from-files`. If the files being read are a result of environment variable
+expansion, the order that the files are read are not guaranteed to be stable (or in the order supplied). Be cautious
+of this if the environment files have define overlapping variables.
#### Example
inject-version:
- type: inject
- file: version.txt
+ type: environment
+ from-file: version.txt
prefix: "APP_"
----
--- a/docker/docker.go Wed Oct 11 20:48:50 2017 -0500
+++ b/docker/docker.go Fri Oct 13 12:04:21 2017 -0500
@@ -25,17 +25,17 @@
var (
Tasks = map[string]tasks.Task{
- "build": &Build{},
- "export": &Export{},
- "import": &Import{},
- "login": &Login{},
- "logout": &Logout{},
- "pull": &Pull{},
- "push": &Push{},
- "remove": &Remove{},
- "run": &Run{},
- "tag": &Tag{},
- "inject": &Inject{},
+ "build": &Build{},
+ "export": &Export{},
+ "import": &Import{},
+ "login": &Login{},
+ "logout": &Logout{},
+ "pull": &Pull{},
+ "push": &Push{},
+ "remove": &Remove{},
+ "run": &Run{},
+ "tag": &Tag{},
+ "environment": &Environment{},
}
dockerTemplate = `docker {{if .DockerConfig}}--config {{.DockerConfig}} {{end}}`
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docker/environment.go Fri Oct 13 12:04:21 2017 -0500
@@ -0,0 +1,141 @@
+/*
+ * Convey
+ * Copyright 2016-2017 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package docker
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/aphistic/gomol"
+
+ "bitbucket.org/rw_grim/convey/environment"
+ "bitbucket.org/rw_grim/convey/state"
+ "bitbucket.org/rw_grim/convey/tasks"
+ "bitbucket.org/rw_grim/convey/yaml"
+)
+
+type Environment struct {
+ File string `yaml:"from-file"`
+ Files yaml.StringOrSlice `yaml:"from-files"`
+ Prefix string `yaml:"prefix"`
+}
+
+func (e *Environment) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
+ fullEnv := environment.Merge(env, st.GetEnv())
+
+ prefix, err := environment.Mapper(e.Prefix, fullEnv)
+ if err != nil {
+ return err
+ }
+
+ files, err := st.MapSlice(e.Files, fullEnv)
+ if err != nil {
+ return err
+ }
+
+ // Create a temp directory we can export files from the current
+ // container into. We can't read directly from the volume, and
+ // we don't want to require reading only environment files on
+ // the host.
+
+ tmpDir, err := ioutil.TempDir("", "convey-environment-")
+ if err != nil {
+ return err
+ }
+ defer os.RemoveAll(tmpDir)
+
+ for _, file := range files {
+ // Export the file into the temp directory and maintain the
+ // structure of the file (for ease of error messages, so we
+ // get the file a/b/c/env instead of env).
+ dest := filepath.Clean(filepath.Join(tmpDir, file))
+
+ if err = exportFile(name, st.Workspace.Name(), file, dest, st); err != nil {
+ return err
+ }
+
+ // Process the entries of the file and apply them to the
+ // state's base environment immediately.
+
+ entries, err := processFile(dest, file, prefix)
+ if err != nil {
+ return err
+ }
+
+ st.MergeEnv(entries)
+ }
+
+ return nil
+}
+
+func processFile(path, name, prefix string) ([]string, error) {
+ data, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read environment file '%s'", name)
+ }
+
+ entries := []string{}
+ for _, line := range strings.Split(string(data), "\n") {
+ // Allow blank lines
+ if len(strings.TrimSpace(line)) == 0 {
+ continue
+ }
+
+ // Each non-empty line requires the form key=val. Split the
+ // key and the val, then uppercase the key and add the prefix.
+ // We don't care what form val takes, it will be treated as a
+ // string (does not need to be quoted).
+
+ parts := strings.SplitN(line, "=", 2)
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("malformed entry in environments file '%s'", line)
+ }
+
+ var (
+ key = strings.TrimSpace(parts[0])
+ val = strings.TrimSpace(parts[1])
+ )
+
+ if len(key) == 0 {
+ return nil, fmt.Errorf("malformed entry in environments file '%s'", line)
+ }
+
+ entries = append(entries, fmt.Sprintf("%s%s=%s", prefix, strings.ToUpper(key), val))
+ }
+
+ return entries, nil
+}
+
+func (e *Environment) New() tasks.Task {
+ return &Environment{}
+}
+
+func (e *Environment) Valid() error {
+ if e.File != "" {
+ e.Files = append([]string{e.File}, e.Files...)
+ }
+
+ if len(e.Files) == 0 {
+ return errNoFilesEnvironment
+ }
+
+ return nil
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docker/environment_test.go Fri Oct 13 12:04:21 2017 -0500
@@ -0,0 +1,58 @@
+/*
+ * Convey
+ * Copyright 2016-2017 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package docker
+
+import (
+ "github.com/aphistic/sweet"
+ "github.com/go-yaml/yaml"
+ . "github.com/onsi/gomega"
+
+ cYaml "bitbucket.org/rw_grim/convey/yaml"
+)
+
+func (s *dockerSuite) TestEnvironment(t sweet.T) {
+ i := &Environment{Files: cYaml.StringOrSlice{"foo"}}
+ Expect(i.Valid()).To(BeNil())
+}
+
+func (s *dockerSuite) TestEnvironmentFilesRequired(t sweet.T) {
+ i := &Environment{}
+ Expect(i.Valid()).To(MatchError(errNoFilesEnvironment))
+}
+
+func (s *dockerSuite) TestEnvironmentUnmarshalString(t sweet.T) {
+ data := `from-files: filename`
+
+ imp := Environment{}
+ err := yaml.Unmarshal([]byte(data), &imp)
+
+ Expect(err).To(BeNil())
+ Expect(imp.Files).To(Equal(cYaml.StringOrSlice{"filename"}))
+}
+
+func (s *dockerSuite) TestEnvironmentUnmarshalStringSlice(t sweet.T) {
+ data := `from-files:
+ - filename1
+ - filename2`
+
+ imp := Environment{}
+ err := yaml.Unmarshal([]byte(data), &imp)
+
+ Expect(err).To(BeNil())
+ Expect(imp.Files).To(Equal(cYaml.StringOrSlice{"filename1", "filename2"}))
+}
--- a/docker/export_test.go Wed Oct 11 20:48:50 2017 -0500
+++ b/docker/export_test.go Fri Oct 13 12:04:21 2017 -0500
@@ -18,50 +18,19 @@
package docker
import (
- . "github.com/onsi/gomega"
-
- "github.com/aphistic/sweet"
- "github.com/go-yaml/yaml"
-
- cYaml "bitbucket.org/rw_grim/convey/yaml"
+ "errors"
)
-func (s *dockerSuite) TestExport(t sweet.T) {
- e := &Export{Files: cYaml.StringOrSlice{"foo"}}
- Expect(e.Valid()).To(BeNil())
-}
-
-func (s *dockerSuite) TestExportFilesRequired(t sweet.T) {
- e := &Export{}
- Expect(e.Valid()).To(MatchError(errNoFiles))
-}
-
-func (s *dockerSuite) TestExportFilesDoesNotAllowWildcardWithDestination(t sweet.T) {
- e := Export{
- Files: cYaml.StringOrSlice{"*:/foo"},
- }
-
- Expect(e.Valid()).To(MatchError(errWildcardWithDestination))
-}
-
-func (s *dockerSuite) TestExportUnmarshalString(t sweet.T) {
- data := `files: filename`
-
- exp := Export{}
- err := yaml.Unmarshal([]byte(data), &exp)
-
- Expect(err).To(BeNil())
- Expect(exp.Files).To(Equal(cYaml.StringOrSlice{"filename"}))
-}
-
-func (s *dockerSuite) TestExportUnmarshalStringSlice(t sweet.T) {
- data := `files:
- - filename1
- - filename2`
-
- exp := Export{}
- err := yaml.Unmarshal([]byte(data), &exp)
-
- Expect(err).To(BeNil())
- Expect(exp.Files).To(Equal(cYaml.StringOrSlice{"filename1", "filename2"}))
-}
+var (
+ errNoDockerFile = errors.New("no dockerfile specified")
+ errNoTag = errors.New("no tag specified")
+ errWildcardWithDestination = errors.New("file list contains a wildcard with a destination")
+ errNoFiles = errors.New("no files specified")
+ errNoServer = errors.New("no server specified")
+ errNoUsername = errors.New("no username specified")
+ errNoImage = errors.New("no image specified")
+ errNoImages = errors.New("no images specified")
+ errNoSourceTag = errors.New("no source tag specified")
+ errNoDestinationTags = errors.New("no destination tags specified")
+ errNoFilesEnvironment = errors.New("no environment files specified")
+)
--- a/docker/inject.go Wed Oct 11 20:48:50 2017 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-/*
- * Convey
- * Copyright 2016-2017 Gary Kramlich <grim@reaperworld.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package docker
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/aphistic/gomol"
-
- "bitbucket.org/rw_grim/convey/environment"
- "bitbucket.org/rw_grim/convey/state"
- "bitbucket.org/rw_grim/convey/tasks"
- "bitbucket.org/rw_grim/convey/yaml"
-)
-
-type Inject struct {
- File string `yaml:"file"`
- Files yaml.StringOrSlice `yaml:"files"`
- Prefix string `yaml:"prefix"`
-}
-
-func (i *Inject) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.GetEnv())
-
- prefix, err := environment.Mapper(i.Prefix, fullEnv)
- if err != nil {
- return err
- }
-
- files, err := st.MapSlice(i.Files, fullEnv)
- if err != nil {
- return err
- }
-
- // Create a temp directory we can export files from the current
- // container into. We can't read directly from the volume, and
- // we don't want to require reading only environment files on
- // the host.
-
- tmpDir, err := ioutil.TempDir("", "convey-inject-")
- if err != nil {
- return err
- }
- defer os.RemoveAll(tmpDir)
-
- for _, file := range files {
- // Export the file into the temp directory and maintain the
- // structure of the file (for ease of error messages, so we
- // get the file a/b/c/env instead of env).
- dest := filepath.Clean(filepath.Join(tmpDir, file))
-
- if err = exportFile(name, st.Workspace.Name(), file, dest, st); err != nil {
- return err
- }
-
- // Process the entries of the file and apply them to the
- // state's base environment immediately.
-
- entries, err := processFile(dest, file, prefix)
- if err != nil {
- return err
- }
-
- st.MergeEnv(entries)
- }
-
- return nil
-}
-
-func processFile(path, name, prefix string) ([]string, error) {
- data, err := ioutil.ReadFile(path)
- if err != nil {
- return nil, fmt.Errorf("failed to read environment file '%s'", name)
- }
-
- entries := []string{}
- for _, line := range strings.Split(string(data), "\n") {
- // Allow blank lines
- if len(strings.TrimSpace(line)) == 0 {
- continue
- }
-
- // Each non-empty line requires the form key=val. Split the
- // key and the val, then uppercase the key and add the prefix.
- // We don't care what form val takes, it will be treated as a
- // string (does not need to be quoted).
-
- parts := strings.SplitN(line, "=", 2)
- if len(parts) != 2 {
- return nil, fmt.Errorf("malformed entry in environments file '%s'", line)
- }
-
- var (
- key = strings.TrimSpace(parts[0])
- val = strings.TrimSpace(parts[1])
- )
-
- if len(key) == 0 {
- return nil, fmt.Errorf("malformed entry in environments file '%s'", line)
- }
-
- entries = append(entries, fmt.Sprintf("%s%s=%s", prefix, strings.ToUpper(key), val))
- }
-
- return entries, nil
-}
-
-func (i *Inject) New() tasks.Task {
- return &Inject{}
-}
-
-func (i *Inject) Valid() error {
- if i.File != "" {
- i.Files = append([]string{i.File}, i.Files...)
- }
-
- if len(i.Files) == 0 {
- return errNoFilesInject
- }
-
- return nil
-}
--- a/docker/inject_test.go Wed Oct 11 20:48:50 2017 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * Convey
- * Copyright 2016-2017 Gary Kramlich <grim@reaperworld.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package docker
-
-import (
- "github.com/aphistic/sweet"
- "github.com/go-yaml/yaml"
- . "github.com/onsi/gomega"
-
- cYaml "bitbucket.org/rw_grim/convey/yaml"
-)
-
-func (s *dockerSuite) TestInject(t sweet.T) {
- i := &Inject{Files: cYaml.StringOrSlice{"foo"}}
- Expect(i.Valid()).To(BeNil())
-}
-
-func (s *dockerSuite) TestInjectFilesRequired(t sweet.T) {
- i := &Inject{}
- Expect(i.Valid()).To(MatchError(errNoFilesInject))
-}
-
-func (s *dockerSuite) TestInjectUnmarshalString(t sweet.T) {
- data := `files: filename`
-
- imp := Inject{}
- err := yaml.Unmarshal([]byte(data), &imp)
-
- Expect(err).To(BeNil())
- Expect(imp.Files).To(Equal(cYaml.StringOrSlice{"filename"}))
-}
-
-func (s *dockerSuite) TestInjectUnmarshalStringSlice(t sweet.T) {
- data := `files:
- - filename1
- - filename2`
-
- imp := Inject{}
- err := yaml.Unmarshal([]byte(data), &imp)
-
- Expect(err).To(BeNil())
- Expect(imp.Files).To(Equal(cYaml.StringOrSlice{"filename1", "filename2"}))
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/environment-from-file.yml Fri Oct 13 12:04:21 2017 -0500
@@ -0,0 +1,56 @@
+# This plan shows how the inject plan can add additional
+# environment variables accessible to your tasks during
+# runtime. Running this with `-e X_FOO=4`, the output
+# will be (in two parts):
+#
+# - X_FOO=4
+# - X_BAR=5
+# - X_BAZ=
+#
+# - X_FOO=4
+# - X_BAR=2
+# - X_BAZ=3
+#
+# Notice that before the inject task, only X_FOO and X_BAR
+# were set by the command line argument and the global env
+# block, respectively. Afterwards, X_FOO's value did not
+# change (as command line parameters take have precedence),
+# X_BAR is overwritten, and X_BAZ is newly added.
+
+environment:
+ - X_BAR=5
+
+tasks:
+ import:
+ type: import
+ files: .
+
+ generate:
+ image: gliderlabs/alpine:edge
+ script:
+ - echo "foo=1" >> env
+ - echo "bar=2" >> env
+ - echo "baz=3" >> env
+ workdir: /workspace
+
+ inject:
+ type: environment
+ from-file: env
+ prefix: "X_"
+
+ print:
+ image: gliderlabs/alpine:edge
+ script:
+ - echo "X_FOO=${X_FOO}"
+ - echo "X_BAR=${X_BAR}"
+ - echo "X_BAZ=${X_BAZ}"
+
+plans:
+ default:
+ stages:
+ - tasks:
+ - import
+ - generate
+ - print
+ - inject
+ - print
--- a/examples/inject.yml Wed Oct 11 20:48:50 2017 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-# This plan shows how the inject plan can add additional
-# environment variables accessible to your tasks during
-# runtime. Running this with `-e X_FOO=4`, the output
-# will be (in two parts):
-#
-# - X_FOO=4
-# - X_BAR=5
-# - X_BAZ=
-#
-# - X_FOO=4
-# - X_BAR=2
-# - X_BAZ=3
-#
-# Notice that before the inject task, only X_FOO and X_BAR
-# were set by the command line argument and the global env
-# block, respectively. Afterwards, X_FOO's value did not
-# change (as command line parameters take have precedence),
-# X_BAR is overwritten, and X_BAZ is newly added.
-
-environment:
- - X_BAR=5
-
-tasks:
- import:
- type: import
- files: .
-
- generate:
- image: gliderlabs/alpine:edge
- script:
- - echo "foo=1" >> env
- - echo "bar=2" >> env
- - echo "baz=3" >> env
- workdir: /workspace
-
- inject:
- type: inject
- files: env
- prefix: "X_"
-
- print:
- image: gliderlabs/alpine:edge
- script:
- - echo "X_FOO=${X_FOO}"
- - echo "X_BAR=${X_BAR}"
- - echo "X_BAZ=${X_BAZ}"
-
-plans:
- default:
- stages:
- - tasks:
- - import
- - generate
- - print
- - inject
- - print