1
0
mirror of https://github.com/drone/drone-cli.git synced 2024-11-23 01:11:57 +01:00

remove default subpath from workspace volume

This commit is contained in:
Brad Rydzewski 2019-01-11 13:58:36 -08:00
parent cb28d6e451
commit 0dc2f87702
10 changed files with 130 additions and 381 deletions

10
Gopkg.lock generated

@ -43,12 +43,6 @@
revision = "85a78806aa1b4707d1dbace9be592cf1ece91ab3"
version = "v1.1.1"
[[projects]]
branch = "master"
name = "github.com/dchest/uniuri"
packages = ["."]
revision = "8902c56451e9b58ff940bbe5fec35d5f9c04584a"
[[projects]]
name = "github.com/docker/distribution"
packages = [
@ -117,7 +111,7 @@
"yaml/pretty",
"yaml/signer"
]
revision = "53f97a4f989bfcce0d26c1f582f44528cdcf4b50"
revision = "dd25dd275e8c82f38f3110cfa564f83d34329685"
[[projects]]
name = "github.com/drone/envsubst"
@ -293,6 +287,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "dd36c9a07b2371502d4f87936691baf9293f8c7cdffc29fdd27cf012cf0ca954"
inputs-digest = "1fa068f17979b5ab98e27b61d8509f666335ceaa20176693cf5bd114bb5a2e1a"
solver-name = "gps-cdcl"
solver-version = 1

@ -7,6 +7,7 @@ import (
"log"
"net/url"
"os"
"strconv"
"time"
"github.com/drone/envsubst"
@ -262,30 +263,14 @@ func exec(c *cli.Context) error {
c.StringSlice("volume"),
),
}
// the user has the option to disable the git clone
// if the pipeline is being executed on the local
// codebase.
if c.Bool("clone") == false {
// HACK(bradrydzewski) If the workspace defines a
// sub-path we append a host volume mount. Else we
// replace the empty dir workspace mount with a host
// volume mount.
switch pipeline.Workspace.Path {
case "", "/", "\\":
default:
}
pwd, _ := os.Getwd()
comp.WorkspaceMountFunc = compiler.MountHostWorkspace
comp.WorkspaceFunc = compiler.CreateHostWorkspace(pwd)
}
comp.TransformFunc = transform.Combine(transforms...)
ir := comp.Compile(pipeline)
// the user has the option to disable the git clone
// if the pipeline is being executed on the local
// codebase.
if c.Bool("clone") == false {
pwd, _ := os.Getwd()
mountWorkspace(ir, pwd)
}
ctx, cancel := context.WithTimeout(
context.Background(),
c.Duration("timeout"),
@ -303,6 +288,30 @@ func exec(c *cli.Context) error {
// creates a hook to print the step output to stdout,
// with per-step color coding if a tty.
hooks := &runtime.Hook{}
hooks.BeforeEach = func(s *runtime.State) error {
s.Step.Envs["CI_BUILD_STATUS"] = "success"
s.Step.Envs["CI_BUILD_STARTED"] = strconv.FormatInt(s.Runtime.Time, 10)
s.Step.Envs["CI_BUILD_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10)
s.Step.Envs["DRONE_BUILD_STATUS"] = "success"
s.Step.Envs["DRONE_BUILD_STARTED"] = strconv.FormatInt(s.Runtime.Time, 10)
s.Step.Envs["DRONE_BUILD_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10)
s.Step.Envs["CI_JOB_STATUS"] = "success"
s.Step.Envs["CI_JOB_STARTED"] = strconv.FormatInt(s.Runtime.Time, 10)
s.Step.Envs["CI_JOB_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10)
s.Step.Envs["DRONE_JOB_STATUS"] = "success"
s.Step.Envs["DRONE_JOB_STARTED"] = strconv.FormatInt(s.Runtime.Time, 10)
s.Step.Envs["DRONE_JOB_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10)
if s.Runtime.Error != nil {
s.Step.Envs["CI_BUILD_STATUS"] = "failure"
s.Step.Envs["CI_JOB_STATUS"] = "failure"
s.Step.Envs["DRONE_BUILD_STATUS"] = "failure"
s.Step.Envs["DRONE_JOB_STATUS"] = "failure"
}
return nil
}
hooks.GotLine = term.WriteLine(os.Stdout)
if tty {
hooks.GotLine = term.WriteLinePretty(

@ -1,72 +0,0 @@
package exec
import (
"path/filepath"
"runtime"
"strings"
"github.com/dchest/uniuri"
"github.com/drone/drone-runtime/engine"
)
// helper function mounts the directory (typicall the
// current working directory) as the workspace in all
// runtime containers. The ensure the working directory
// (your .git repository) is available to your local
// execution.
func mountWorkspace(spec *engine.Spec, pwd string) {
// mount the working directory as a host-machine
// volume to expose the working directory inside
// the containers.
spec.Docker.Volumes = append(
spec.Docker.Volumes,
&engine.Volume{
Metadata: engine.Metadata{
UID: uniuri.New(),
Name: "_local",
},
HostPath: &engine.VolumeHostPath{
Path: pwd,
},
},
)
// mount the working directory volume into the
// workspace of every container.
for _, container := range spec.Steps {
container.Volumes = append(
container.Volumes,
&engine.VolumeMount{
Name: "_local",
// HACK(bradrydzewski) this feels like a
// hack. It would be nice if we could make
// this an official transform function in
// the transform.
Path: container.Envs["DRONE_WORKSPACE"],
},
)
}
}
// helper funciton normalizes the mount path based on the
// host operating system. Specifically we need this for
// windows environments.
func normalizeMountPath(path string) string {
switch runtime.GOOS {
case "windows":
return normalizeMountPathWin(path)
default:
return path
}
}
// helper funciton normalizes the mount path for windows
// environments.
func normalizeMountPathWin(path string) string {
base := filepath.VolumeName(path)
if len(base) == 2 {
path = strings.TrimPrefix(path, base)
base = strings.ToLower(strings.TrimSuffix(base, ":"))
return "/" + base + filepath.ToSlash(path)
}
return filepath.ToSlash(path)
}

@ -1,48 +0,0 @@
package exec
import (
"testing"
"github.com/drone/drone-runtime/engine"
)
func TestMountWorkspace(t *testing.T) {
step := &engine.Step{
Envs: map[string]string{
"DRONE_WORKSPACE": "/workspace",
},
}
spec := &engine.Spec{
Docker: &engine.DockerConfig{},
Steps: []*engine.Step{step},
}
mountWorkspace(spec, "/path/on/host")
if len(spec.Docker.Volumes) == 0 {
t.Errorf("Expect volume mounted on host")
return
}
volume := spec.Docker.Volumes[0]
if got, want := volume.HostPath.Path, "/path/on/host"; got != want {
t.Errorf("Want volume mount %s, got %s", want, got)
}
if got, want := volume.Metadata.Name, "_local"; got != want {
t.Errorf("Want volume name %s, got %s", want, got)
}
if volume.Metadata.UID == "" {
t.Errorf("Want volume UID got empty string")
}
if len(step.Volumes) == 0 {
t.Errorf("Excpect container volume mount")
return
}
mount := step.Volumes[0]
if got, want := mount.Path, "/workspace"; got != want {
t.Errorf("Want volume mount %s, got %s", want, got)
}
if got, want := mount.Name, "_local"; got != want {
t.Errorf("Want volume name %s, got %s", want, got)
}
}

@ -1,7 +0,0 @@
language: go
go:
- 1.2
- 1.3
- 1.4
- tip

@ -1,97 +0,0 @@
Package uniuri
=====================
[![Build Status](https://travis-ci.org/dchest/uniuri.svg)](https://travis-ci.org/dchest/uniuri)
```go
import "github.com/dchest/uniuri"
```
Package uniuri generates random strings good for use in URIs to identify
unique objects.
Example usage:
```go
s := uniuri.New() // s is now "apHCJBl7L1OmC57n"
```
A standard string created by New() is 16 bytes in length and consists of
Latin upper and lowercase letters, and numbers (from the set of 62 allowed
characters), which means that it has ~95 bits of entropy. To get more
entropy, you can use NewLen(UUIDLen), which returns 20-byte string, giving
~119 bits of entropy, or any other desired length.
Functions read from crypto/rand random source, and panic if they fail to
read from it.
Constants
---------
```go
const (
// StdLen is a standard length of uniuri string to achive ~95 bits of entropy.
StdLen = 16
// UUIDLen is a length of uniuri string to achive ~119 bits of entropy, closest
// to what can be losslessly converted to UUIDv4 (122 bits).
UUIDLen = 20
)
```
Variables
---------
```go
var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
```
StdChars is a set of standard characters allowed in uniuri string.
Functions
---------
### func New
```go
func New() string
```
New returns a new random string of the standard length, consisting of
standard characters.
### func NewLen
```go
func NewLen(length int) string
```
NewLen returns a new random string of the provided length, consisting of
standard characters.
### func NewLenChars
```go
func NewLenChars(length int, chars []byte) string
```
NewLenChars returns a new random string of the provided length, consisting
of the provided byte slice of allowed characters (maximum 256).
Public domain dedication
------------------------
Written in 2011-2014 by Dmitry Chestnykh
The author(s) have dedicated all copyright and related and
neighboring rights to this software to the public domain
worldwide. Distributed without any warranty.
http://creativecommons.org/publicdomain/zero/1.0/

@ -1,81 +0,0 @@
// Written in 2011-2014 by Dmitry Chestnykh
//
// The author(s) have dedicated all copyright and related and
// neighboring rights to this software to the public domain
// worldwide. Distributed without any warranty.
// http://creativecommons.org/publicdomain/zero/1.0/
// Package uniuri generates random strings good for use in URIs to identify
// unique objects.
//
// Example usage:
//
// s := uniuri.New() // s is now "apHCJBl7L1OmC57n"
//
// A standard string created by New() is 16 bytes in length and consists of
// Latin upper and lowercase letters, and numbers (from the set of 62 allowed
// characters), which means that it has ~95 bits of entropy. To get more
// entropy, you can use NewLen(UUIDLen), which returns 20-byte string, giving
// ~119 bits of entropy, or any other desired length.
//
// Functions read from crypto/rand random source, and panic if they fail to
// read from it.
package uniuri
import "crypto/rand"
const (
// StdLen is a standard length of uniuri string to achive ~95 bits of entropy.
StdLen = 16
// UUIDLen is a length of uniuri string to achive ~119 bits of entropy, closest
// to what can be losslessly converted to UUIDv4 (122 bits).
UUIDLen = 20
)
// StdChars is a set of standard characters allowed in uniuri string.
var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
// New returns a new random string of the standard length, consisting of
// standard characters.
func New() string {
return NewLenChars(StdLen, StdChars)
}
// NewLen returns a new random string of the provided length, consisting of
// standard characters.
func NewLen(length int) string {
return NewLenChars(length, StdChars)
}
// NewLenChars returns a new random string of the provided length, consisting
// of the provided byte slice of allowed characters (maximum 256).
func NewLenChars(length int, chars []byte) string {
if length == 0 {
return ""
}
clen := len(chars)
if clen < 2 || clen > 256 {
panic("uniuri: wrong charset length for NewLenChars")
}
maxrb := 255 - (256 % clen)
b := make([]byte, length)
r := make([]byte, length+(length/4)) // storage for random bytes.
i := 0
for {
if _, err := rand.Read(r); err != nil {
panic("uniuri: error reading random bytes: " + err.Error())
}
for _, rb := range r {
c := int(rb)
if c > maxrb {
// Skip this number to avoid modulo bias.
continue
}
b[i] = chars[c%clen]
i++
if i == length {
return string(b)
}
}
}
}

@ -39,13 +39,16 @@ type Compiler struct {
// set defaults, etc.
TransformFunc func(*engine.Spec)
// WorkspaceFunc can be used to customize the default
// workspace paths.
WorkspaceFunc func(*yaml.Pipeline) (base, path, full string)
// WorkspaceFunc can be used to set the workspace volume
// that is created for the entire pipeline. The primary
// use case for this function is running local builds,
// where the workspace is mounted to the host machine
// working directory.
WorkspaceFunc func(*engine.Spec)
// WorkspaceMountFunc can be used to override the default
// workspace volume mount.
WorkspaceMountFunc func(*engine.Spec, string)
WorkspaceMountFunc func(step *engine.Step, base, path, full string)
}
// Compile returns an intermediate representation of the
@ -87,22 +90,12 @@ func (c *Compiler) Compile(from *yaml.Pipeline) *engine.Spec {
// create the default workspace path. If a container
// does not specify a working directory it defaults
// to the workspace path.
base, dir, workspace := c.workspace(from)
base, dir, workspace := createWorkspace(from)
// create the default workspace volume definition.
// the volume will be mounted to each container in
// the pipeline.
spec.Docker.Volumes = append(spec.Docker.Volumes,
&engine.Volume{
Metadata: engine.Metadata{
UID: rand.String(),
Name: workspaceName,
Namespace: namespace,
Labels: map[string]string{},
},
EmptyDir: &engine.VolumeEmptyDir{},
},
)
c.setupWorkspace(spec)
// for each volume defined in the yaml configuration
// file, convert to a runtime volume and append to the
@ -141,8 +134,8 @@ func (c *Compiler) Compile(from *yaml.Pipeline) *engine.Spec {
setupCloneDepth(from, dst)
setupCloneCredentials(spec, dst, c.gitCredentials())
setupWorkingDir(src, dst, workspace)
setupWorkingDirMount(dst, base)
setupWorkspaceEnv(dst, base, dir, workspace)
c.setupWorkspaceMount(dst, base, dir, workspace)
spec.Steps = append(spec.Steps, dst)
}
@ -155,8 +148,8 @@ func (c *Compiler) Compile(from *yaml.Pipeline) *engine.Spec {
// set to run in detached mode.
step.Detach = true
setupWorkingDir(service, step, workspace)
setupWorkingDirMount(step, base)
setupWorkspaceEnv(step, base, dir, workspace)
c.setupWorkspaceMount(step, base, dir, workspace)
// if the skip callback function returns true,
// modify the runtime step to never execute.
if c.skip(service) {
@ -193,8 +186,8 @@ func (c *Compiler) Compile(from *yaml.Pipeline) *engine.Spec {
step = createStep(spec, container)
}
setupWorkingDir(container, step, workspace)
setupWorkingDirMount(step, base)
setupWorkspaceEnv(step, base, dir, workspace)
c.setupWorkspaceMount(step, base, dir, workspace)
// if the skip callback function returns true,
// modify the runtime step to never execute.
if c.skip(container) {
@ -292,19 +285,19 @@ func (c *Compiler) skip(container *yaml.Container) bool {
return false
}
// return the workspace paths. If the user-defined
// function is nil, default logic is used.
func (c *Compiler) workspace(pipeline *yaml.Pipeline) (base, path, full string) {
func (c *Compiler) setupWorkspace(spec *engine.Spec) {
if c.WorkspaceFunc != nil {
return c.WorkspaceFunc(pipeline)
c.WorkspaceFunc(spec)
return
}
return createWorkspace(pipeline)
CreateWorkspace(spec)
return
}
// func (c *Compiler) workspaceMount(step *engine.Step, base, path, full string) {
// if c.WorkspaceMountFunc != nil {
// c.WorkspaceMountFunc(step, path)
// return
// }
// setupWorkingDirMount(spec, base)
// }
func (c *Compiler) setupWorkspaceMount(step *engine.Step, base, path, full string) {
if c.WorkspaceMountFunc != nil {
c.WorkspaceMountFunc(step, base, path, full)
return
}
MountWorkspace(step, base, path, full)
}

@ -6,10 +6,14 @@ import (
"github.com/drone/drone-runtime/engine"
"github.com/drone/drone-yaml/yaml"
"github.com/drone/drone-yaml/yaml/compiler/internal/rand"
)
// default name for the workspace volume.
const workspaceName = "workspace"
const (
workspacePath = "/drone/src"
workspaceName = "workspace"
workspaceHostName = "host"
)
func setupWorkingDir(src *yaml.Container, dst *engine.Step, path string) {
// if the working directory is already set
@ -27,15 +31,6 @@ func setupWorkingDir(src *yaml.Container, dst *engine.Step, path string) {
dst.WorkingDir = path
}
// helper function mounts the working directory base
// path to the container.
func setupWorkingDirMount(step *engine.Step, path string) {
step.Volumes = append(step.Volumes, &engine.VolumeMount{
Name: workspaceName,
Path: path,
})
}
// helper function appends the workspace base and
// path to the step's list of environment variables.
func setupWorkspaceEnv(step *engine.Step, base, path, full string) {
@ -67,10 +62,7 @@ func createWorkspace(from *yaml.Pipeline) (base, path, full string) {
base = from.Workspace.Base
path = from.Workspace.Path
if base == "" {
base = "/drone"
}
if path == "" {
path = "src"
base = workspacePath
}
full = unixpath.Join(base, path)
@ -81,3 +73,72 @@ func createWorkspace(from *yaml.Pipeline) (base, path, full string) {
}
return base, path, full
}
//
//
//
// CreateWorkspace creates the workspace volume as
// an empty directory mount.
func CreateWorkspace(spec *engine.Spec) {
spec.Docker.Volumes = append(spec.Docker.Volumes,
&engine.Volume{
Metadata: engine.Metadata{
UID: rand.String(),
Name: workspaceName,
Namespace: spec.Metadata.Namespace,
Labels: map[string]string{},
},
EmptyDir: &engine.VolumeEmptyDir{},
},
)
}
// CreateHostWorkspace returns a WorkspaceFunc that
// mounts a host machine volume as the pipeline
// workspace.
func CreateHostWorkspace(workdir string) func(*engine.Spec) {
return func(spec *engine.Spec) {
CreateWorkspace(spec)
spec.Docker.Volumes = append(
spec.Docker.Volumes,
&engine.Volume{
Metadata: engine.Metadata{
UID: rand.String(),
Name: workspaceHostName,
},
HostPath: &engine.VolumeHostPath{
Path: workdir,
},
},
)
}
}
//
//
//
// MountWorkspace is a WorkspaceFunc that mounts the
// default workspace volume to the pipeline step.
func MountWorkspace(step *engine.Step, base, path, full string) {
step.Volumes = append(step.Volumes, &engine.VolumeMount{
Name: workspaceName,
Path: base,
})
}
// MountHostWorkspace is a WorkspaceFunc that mounts
// the default workspace and host volume to the pipeline.
func MountHostWorkspace(step *engine.Step, base, path, full string) {
step.Volumes = append(step.Volumes, &engine.VolumeMount{
Name: workspaceHostName,
Path: full,
})
if path != "" {
step.Volumes = append(step.Volumes, &engine.VolumeMount{
Name: workspaceName,
Path: base,
})
}
}

@ -28,9 +28,6 @@ func Convert(b []byte, ref string) ([]byte, error) {
pipeline.Name = "default"
pipeline.Kind = "pipeline"
pipeline.Workspace.Base = "/drone"
pipeline.Workspace.Path = "/src"
//
// clone
//