diff --git a/command/daemon/daemon.go b/command/daemon/daemon.go index 6d4ec51..91ad6f8 100644 --- a/command/daemon/daemon.go +++ b/command/daemon/daemon.go @@ -67,7 +67,7 @@ func (c *daemonCommand) run(*kingpin.ParseContext) error { ), ) - engine, err := engine.New(config.Keypair.Public, config.Keypair.Private) + engine, err := engine.NewEnv() if err != nil { return err } diff --git a/command/exec.go b/command/exec.go index 4d73168..366617e 100644 --- a/command/exec.go +++ b/command/exec.go @@ -220,7 +220,7 @@ func (c *execCommand) run(*kingpin.ParseContext) error { ), ) - engine, err := engine.New(c.PublicKey, c.PrivateKey) + engine, err := engine.NewEnv() if err != nil { return err } diff --git a/engine/compiler/compiler.go b/engine/compiler/compiler.go index fc77a76..9cca5f4 100644 --- a/engine/compiler/compiler.go +++ b/engine/compiler/compiler.go @@ -369,6 +369,30 @@ func (c *Compiler) Compile(ctx context.Context, args Args) *engine.Spec { } } + // append volumes + for _, v := range args.Pipeline.Volumes { + id := random() + src := new(engine.Volume) + if v.EmptyDir != nil { + src.EmptyDir = &engine.VolumeEmptyDir{ + ID: id, + Name: v.Name, + Medium: v.EmptyDir.Medium, + SizeLimit: int64(v.EmptyDir.SizeLimit), + Labels: labels, + } + } else if v.HostPath != nil { + src.HostPath = &engine.VolumeHostPath{ + ID: id, + Name: v.Name, + Path: v.HostPath.Path, + } + } else { + continue + } + spec.Volumes = append(spec.Volumes, src) + } + return spec } diff --git a/engine/compiler/script.go b/engine/compiler/script.go index f9748d3..0947b82 100644 --- a/engine/compiler/script.go +++ b/engine/compiler/script.go @@ -30,6 +30,6 @@ func setupScriptWindows(src *resource.Step, dst *engine.Step) { func setupScriptPosix(src *resource.Step, dst *engine.Step) { dst.Entrypoint = []string{"/bin/sh", "-c"} - dst.Command = []string{"echo $DRONE_SCRIPT | /bin/sh -e"} + dst.Command = []string{`echo "$DRONE_SCRIPT" | /bin/sh`} dst.Envs["DRONE_SCRIPT"] = shell.Script(src.Commands) } diff --git a/engine/compiler/shell/shell.go b/engine/compiler/shell/shell.go index e779221..2f6aa26 100644 --- a/engine/compiler/shell/shell.go +++ b/engine/compiler/shell/shell.go @@ -34,9 +34,8 @@ func Script(commands []string) string { // optionScript is a helper script this is added to the build // to set shell options, in this case, to exit on error. const optionScript = ` -if [[ ! -z "${DRONE_NETRC_FILE}" ]]; then +if [ ! -z "${DRONE_NETRC_FILE}" ]; then echo $DRONE_NETRC_FILE > $HOME/.netrc -EOF fi unset DRONE_SCRIPT diff --git a/engine/compiler/step.go b/engine/compiler/step.go index 9ed1d19..291b445 100644 --- a/engine/compiler/step.go +++ b/engine/compiler/step.go @@ -43,7 +43,7 @@ func createStep(spec *resource.Pipeline, src *resource.Step) *engine.Step { Networks: nil, // set in compiler.go Files: nil, // set below Volumes: nil, // set below - // Devices: nil, // TODO + Devices: nil, // see below // Resources: toResources(src), // TODO } @@ -55,6 +55,14 @@ func createStep(spec *resource.Pipeline, src *resource.Step) *engine.Step { }) } + // appends the devices to the container def. + for _, vol := range src.Devices { + dst.Devices = append(dst.Devices, &engine.VolumeDevice{ + Name: vol.Name, + DevicePath: vol.DevicePath, + }) + } + // appends the settings variables to the // container definition. for key, value := range src.Settings { @@ -82,19 +90,6 @@ func createStep(spec *resource.Pipeline, src *resource.Step) *engine.Step { } } - // // if the step specifies shell commands we generate a - // // script. The script is copied to the container at - // // runtime (or mounted as a config map) and then executed - // // as the entrypoint. - // if len(src.Commands) > 0 { - // switch spec.Platform.OS { - // case "windows": - // setupScriptWin(spec, dst, src) - // default: - // setupScript(spec, dst, src) - // } - // } - // set the pipeline step run policy. steps run on // success by default, but may be optionally configured // to run on failure. diff --git a/engine/compiler/workspace.go b/engine/compiler/workspace.go index 0f369e9..4b5d82f 100644 --- a/engine/compiler/workspace.go +++ b/engine/compiler/workspace.go @@ -22,7 +22,12 @@ func createWorkspace(from *resource.Pipeline) (base, path, full string) { base = from.Workspace.Base path = from.Workspace.Path if base == "" { - base = workspacePath + if strings.HasPrefix(path, "/") { + base = path + path = "" + } else { + base = workspacePath + } } full = stdpath.Join(base, path) diff --git a/engine/convert.go b/engine/convert.go index f351c90..a170ae8 100644 --- a/engine/convert.go +++ b/engine/convert.go @@ -36,7 +36,7 @@ func toConfig(spec *Spec, step *Step) *container.Config { } if len(step.Entrypoint) != 0 { - config.Cmd = step.Entrypoint + config.Entrypoint = step.Entrypoint } if len(step.Command) != 0 { config.Cmd = step.Command @@ -117,7 +117,7 @@ func toNetConfig(spec *Spec, proc *Step) *network.NetworkingConfig { func toDeviceSlice(spec *Spec, step *Step) []container.DeviceMapping { var to []container.DeviceMapping for _, mount := range step.Devices { - device, ok := LookupVolume(spec, mount.Name) + device, ok := lookupVolume(spec, mount.Name) if !ok { continue } @@ -141,7 +141,7 @@ func toDeviceSlice(spec *Spec, step *Step) []container.DeviceMapping { func toVolumeSet(spec *Spec, step *Step) map[string]struct{} { set := map[string]struct{}{} for _, mount := range step.Volumes { - volume, ok := LookupVolume(spec, mount.Name) + volume, ok := lookupVolume(spec, mount.Name) if !ok { continue } @@ -166,7 +166,7 @@ func toVolumeSlice(spec *Spec, step *Step) []string { // to get it working with data volumes. var to []string for _, mount := range step.Volumes { - volume, ok := LookupVolume(spec, mount.Name) + volume, ok := lookupVolume(spec, mount.Name) if !ok { continue } @@ -174,7 +174,7 @@ func toVolumeSlice(spec *Spec, step *Step) []string { continue } if isDataVolume(volume) { - path := volume.Metadata.UID + ":" + mount.Path + path := volume.EmptyDir.ID + ":" + mount.Path to = append(to, path) } if isBindMount(volume) { @@ -190,7 +190,7 @@ func toVolumeSlice(spec *Spec, step *Step) []string { func toVolumeMounts(spec *Spec, step *Step) []mount.Mount { var mounts []mount.Mount for _, target := range step.Volumes { - source, ok := LookupVolume(spec, target.Name) + source, ok := lookupVolume(spec, target.Name) if !ok { continue } @@ -284,3 +284,16 @@ func isNamedPipe(volume *Volume) bool { return volume.HostPath != nil && strings.HasPrefix(volume.HostPath.Path, `\\.\pipe\`) } + +// helper function returns the named volume. +func lookupVolume(spec *Spec, name string) (*Volume, bool) { + for _, v := range spec.Volumes { + if v.HostPath != nil && v.HostPath.Name == name { + return v, true + } + if v.EmptyDir != nil && v.EmptyDir.Name == name { + return v, true + } + } + return nil, false +} diff --git a/engine/linter/linter.go b/engine/linter/linter.go index 3f24a0f..54f1538 100644 --- a/engine/linter/linter.go +++ b/engine/linter/linter.go @@ -139,6 +139,8 @@ func checkVolumes(pipeline *resource.Pipeline, trusted bool) error { } } switch volume.Name { + case "": + return fmt.Errorf("linter: missing volume name") case "workspace", "_workspace", "_docker_socket": return fmt.Errorf("linter: invalid volume name: %s", volume.Name) } diff --git a/engine/linter/linter_test.go b/engine/linter/linter_test.go index f5719f6..f0fdec6 100644 --- a/engine/linter/linter_test.go +++ b/engine/linter/linter_test.go @@ -30,6 +30,12 @@ func TestLint(t *testing.T) { message: "linter: invalid or missing image", }, // user should not use reserved volume names. + { + path: "testdata/volume_missing_name.yml", + trusted: false, + invalid: true, + message: "linter: missing volume name", + }, { path: "testdata/volume_invalid_name.yml", trusted: false, diff --git a/engine/linter/testdata/volume_missing_name.yml b/engine/linter/testdata/volume_missing_name.yml new file mode 100644 index 0000000..35e5e41 --- /dev/null +++ b/engine/linter/testdata/volume_missing_name.yml @@ -0,0 +1,18 @@ +--- +kind: pipeline +type: docker +name: linux + +steps: +- name: test + image: golang + commands: + - go build + - go test + +services: +- name: database + image: redis + +volumes: +- temp: {} diff --git a/engine/resource/parser.go b/engine/resource/parser.go index c6d3f50..11a27f9 100644 --- a/engine/resource/parser.go +++ b/engine/resource/parser.go @@ -32,7 +32,8 @@ func parse(r *manifest.RawResource) (manifest.Resource, bool, error) { // match returns true if the resource matches the kind and type. func match(r *manifest.RawResource) bool { - return r.Kind == Kind && r.Type == Type + return (r.Kind == Kind && r.Type == Type) || + (r.Kind == Kind && r.Type == "") } func lint(pipeline *Pipeline) error { diff --git a/engine/spec.go b/engine/spec.go index 9387343..393ba8e 100644 --- a/engine/spec.go +++ b/engine/spec.go @@ -27,6 +27,7 @@ type ( CPUSet []string `json:"cpu_set,omitempty"` Detach bool `json:"detach,omitempty"` DependsOn []string `json:"depends_on,omitempty"` + Devices []*VolumeDevice `json:"devices,omitempty"` DNS []string `json:"dns,omitempty"` DNSSearch []string `json:"dns_search,omitempty"` Entrypoint []string `json:"entrypoint,omitempty"` @@ -119,39 +120,11 @@ type ( Labels map[string]string `json:"labels,omitempty"` } - // XVolume that is mounted into the container - XVolume struct { - ID string `json:"id,omitempty"` - Source string `json:"source,omitempty"` - Target string `json:"target,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - } - - volumeDevice struct { - Path string - } - - volumeData struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Path string `json:"target,omitempty"` - Mode uint32 `json:"mode,omitempty"` - } - - volumeBind struct { - Source string `json:"source,omitempty"` - Target string `json:"target,omitempty"` - Readonly bool `json:"readonly,omitempty"` - } - - volumePipe struct { - Source string `json:"source,omitempty"` - Target string `json:"target,omitempty"` - } - - volumeTemp struct { - Size int64 `json:"size,omitempty"` - Path string `json:"path,omitempty"` + // VolumeDevice describes a mapping of a raw block + // device within a container. + VolumeDevice struct { + Name string `json:"name,omitempty"` + DevicePath string `json:"path,omitempty"` } // Network that is created and attached to containers diff --git a/engine/testdata/network_bridge.yml b/engine/testdata/network_bridge.yml new file mode 100644 index 0000000..e69de29 diff --git a/engine/testdata/network_default.yml b/engine/testdata/network_default.yml new file mode 100644 index 0000000..5bda86d --- /dev/null +++ b/engine/testdata/network_default.yml @@ -0,0 +1,21 @@ +kind: pipeline +type: docker +name: default + +clone: + disable: true + +steps: +- name: test + pull: if-not-exists + image: redis + commands: + - sleep 5 + - redis-cli -h redis ping + - redis-cli -h redis set FOO bar + - redis-cli -h redis get FOO + +services: +- name: redis + pull: if-not-exists + image: redis diff --git a/engine/testdata/network_host.yml b/engine/testdata/network_host.yml new file mode 100644 index 0000000..e69de29 diff --git a/engine/testdata/status_failure.yml b/engine/testdata/status_failure.yml new file mode 100644 index 0000000..8c06b08 --- /dev/null +++ b/engine/testdata/status_failure.yml @@ -0,0 +1,15 @@ +kind: pipeline +type: docker +name: default + +clone: + disable: true + +steps: +- name: test + pull: if-not-exists + image: alpine + commands: + - echo hello + - echo world + - exit 1 diff --git a/engine/testdata/status_success.yml b/engine/testdata/status_success.yml new file mode 100644 index 0000000..347b095 --- /dev/null +++ b/engine/testdata/status_success.yml @@ -0,0 +1,15 @@ +kind: pipeline +type: docker +name: default + +clone: + disable: true + +steps: +- name: test + pull: if-not-exists + image: alpine + commands: + - echo hello + - echo world + - exit 0 diff --git a/engine/testdata/volume_host.yml b/engine/testdata/volume_host.yml new file mode 100644 index 0000000..8367a32 --- /dev/null +++ b/engine/testdata/volume_host.yml @@ -0,0 +1,32 @@ +kind: pipeline +type: docker +name: default + +clone: + disable: true + +steps: +- name: write + pull: if-not-exists + image: alpine + volumes: + - name: test + path: /tmp + commands: + - pwd + - echo "hello" > /tmp/greetings.txt + +- name: read + pull: if-not-exists + image: alpine + volumes: + - name: test + path: /tmp + commands: + - pwd + - cat /tmp/greetings.txt + +volumes: +- name: test + host: + path: /tmp/drone/test \ No newline at end of file diff --git a/engine/testdata/volume_mem.yml b/engine/testdata/volume_mem.yml new file mode 100644 index 0000000..21e2fe7 --- /dev/null +++ b/engine/testdata/volume_mem.yml @@ -0,0 +1,24 @@ +kind: pipeline +type: docker +name: default + +clone: + disable: true + +steps: +- name: write + pull: if-not-exists + image: alpine + volumes: + - name: test + path: /tmp/memory + commands: + - ls -la /tmp + - ls -la /tmp/memory + - touch /tmp/memory/hello.txt + - df -T /tmp/memory + +volumes: +- name: test + temp: + medium: memory diff --git a/engine/testdata/volume_temp.yml b/engine/testdata/volume_temp.yml new file mode 100644 index 0000000..d4027e7 --- /dev/null +++ b/engine/testdata/volume_temp.yml @@ -0,0 +1,31 @@ +kind: pipeline +type: docker +name: default + +clone: + disable: true + +steps: +- name: write + pull: if-not-exists + image: alpine + volumes: + - name: test + path: /tmp + commands: + - pwd + - echo "hello" > /tmp/greetings.txt + +- name: read + pull: if-not-exists + image: alpine + volumes: + - name: test + path: /tmp + commands: + - pwd + - cat /tmp/greetings.txt + +volumes: +- name: test + temp: {} diff --git a/engine/testdata/workspace_custom.yml b/engine/testdata/workspace_custom.yml new file mode 100644 index 0000000..8b0fb3f --- /dev/null +++ b/engine/testdata/workspace_custom.yml @@ -0,0 +1,24 @@ +kind: pipeline +type: docker +name: default + +clone: + disable: true + +workspace: + path: /drone/custom/path + +steps: +- name: write + pull: if-not-exists + image: alpine + commands: + - pwd + - echo "hello" > greetings.txt + +- name: read + pull: if-not-exists + image: alpine + commands: + - pwd + - cat greetings.txt diff --git a/engine/testdata/workspace_default.yml b/engine/testdata/workspace_default.yml new file mode 100644 index 0000000..dec20cd --- /dev/null +++ b/engine/testdata/workspace_default.yml @@ -0,0 +1,21 @@ +kind: pipeline +type: docker +name: default + +clone: + disable: true + +steps: +- name: write + pull: if-not-exists + image: alpine + commands: + - echo "hello" > greetings.txt + - df -T /drone/src + +- name: read + pull: if-not-exists + image: alpine + commands: + - pwd + - cat greetings.txt diff --git a/engine/testdata/workspace_legacy.yml b/engine/testdata/workspace_legacy.yml new file mode 100644 index 0000000..c9c2047 --- /dev/null +++ b/engine/testdata/workspace_legacy.yml @@ -0,0 +1,25 @@ +kind: pipeline +type: docker +name: default + +clone: + disable: true + +workspace: + base: /tmp + path: /drone + +steps: +- name: write + pull: if-not-exists + image: alpine + commands: + - pwd + - echo "hello" > /tmp/greetings.txt + +- name: read + pull: if-not-exists + image: alpine + commands: + - pwd + - cat /tmp/greetings.txt