diff --git a/.drone.yml b/.drone.yml index b0b1172..40e336f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,7 +8,7 @@ platform: steps: - name: deps - image: golang:1.19 + image: golang:1.20 commands: - make deps volumes: @@ -16,7 +16,7 @@ steps: path: /go - name: lint - image: golang:1.19 + image: golang:1.20 commands: - make lint volumes: @@ -24,7 +24,7 @@ steps: path: /go - name: test - image: golang:1.19 + image: golang:1.20 commands: - make test volumes: @@ -51,7 +51,7 @@ platform: steps: - name: build - image: techknowlogick/xgo:go-1.19.x + image: techknowlogick/xgo:go-1.20.x commands: - ln -s /drone/src /source - make release @@ -292,6 +292,6 @@ depends_on: --- kind: signature -hmac: 236f7dcfcdbb18ed57935d336f25ec56c34074e872a94682c932422e59def005 +hmac: 383e1b462823db3d79fb155de2bf3b53a4f4380ab036778552011c84fec23344 ... diff --git a/.golangci.yml b/.golangci.yml index 7bb18ea..c6e4e1a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,25 +1,92 @@ linters: - enable: - - gosimple - - deadcode - - typecheck - - govet - - errcheck - - staticcheck - - unused - - structcheck - - varcheck - - dupl - - gofmt - - misspell - - gocritic - - bidichk - - ineffassign - - revive - - gofumpt - - depguard enable-all: false disable-all: true + enable: + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - typecheck + - unused + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + - decorder + - depguard + - dogsled + - dupl + - dupword + - durationcheck + - errchkjson + - errname + - errorlint + - execinquery + - exhaustive + - exportloopref + - forcetypeassert + - ginkgolinter + - gocheckcompilerdirectives + - gochecknoglobals + - gochecknoinits + - gocognit + - goconst + - gocritic + - gocyclo + - godot + - godox + - goerr113 + - gofmt + - gofumpt + - goheader + - goimports + - gomnd + - gomoddirectives + - gomodguard + - goprintffuncname + - gosec + - grouper + - importas + - interfacebloat + - ireturn + - lll + - loggercheck + - maintidx + - makezero + - misspell + - musttag + - nakedret + - nestif + - nilerr + - nilnil + - nlreturn + - noctx + - nolintlint + - nonamedreturns + - nosprintfhostport + - prealloc + - predeclared + - promlinter + - reassign + - revive + # - rowserrcheck + # - sqlclosecheck + # - structcheck + - stylecheck + - tagliatelle + - tenv + - testableexamples + - thelper + - tparallel + - unconvert + - unparam + - usestdlibvars + # - wastedassign + - whitespace + - wsl fast: false run: @@ -28,4 +95,10 @@ run: linters-settings: gofumpt: extra-rules: true - lang-version: "1.18" + lang-version: "1.20" + tagliatelle: + case: + use-field-name: true + rules: + json: snake + yaml: snake diff --git a/Makefile b/Makefile index 2e7458a..9e25a89 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@$(G XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest GENERATE ?= -XGO_VERSION := go-1.19.x +XGO_VERSION := go-1.20.x XGO_TARGETS ?= linux/amd64,linux/arm-6,linux/arm-7,linux/arm64 TARGETOS ?= linux diff --git a/cmd/drone-yaml/main.go b/cmd/drone-yaml/main.go index c916df1..f37ccc8 100644 --- a/cmd/drone-yaml/main.go +++ b/cmd/drone-yaml/main.go @@ -15,6 +15,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) +//nolint:gochecknoglobals var ( format = kingpin.Command("fmt", "format the yaml file") formatSave = format.Flag("save", "save result to source").Short('s').Bool() @@ -25,6 +26,8 @@ var ( lintFile = lint.Arg("source", "source file location").Default(".drone.yml").File() ) +const DefaultFilePerm = 0o640 + func main() { switch kingpin.Parse() { case format.FullCommand(): @@ -36,6 +39,7 @@ func main() { func runFormat() error { f := *formatFile + m, err := yaml.Parse(f) if err != nil { return err @@ -45,23 +49,28 @@ func runFormat() error { pretty.Print(b, m) if *formatSave { - return os.WriteFile(f.Name(), b.Bytes(), 0o644) + return os.WriteFile(f.Name(), b.Bytes(), DefaultFilePerm) } + _, err = io.Copy(os.Stderr, b) + return err } func runLint() error { f := *lintFile + m, err := yaml.Parse(f) if err != nil { return err } + for _, r := range m.Resources { err := linter.Lint(r, *lintPriv) if err != nil { return err } } + return nil } diff --git a/go.mod b/go.mod index 0eec453..eb3032a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/drone/drone-yaml -go 1.19 +go 1.20 require ( github.com/bmatcuk/doublestar/v4 v4.6.0 diff --git a/yaml/build.go b/yaml/build.go index 7256669..279adf0 100644 --- a/yaml/build.go +++ b/yaml/build.go @@ -29,15 +29,18 @@ type ( // UnmarshalYAML implements yaml unmarshalling. func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error { d := new(build) + err := unmarshal(&d.Image) if err != nil { err = unmarshal(d) } + b.Args = d.Args b.CacheFrom = d.CacheFrom b.Context = d.Context b.Dockerfile = d.Dockerfile b.Labels = d.Labels b.Image = d.Image + return err } diff --git a/yaml/cond.go b/yaml/cond.go index bece1f5..763519b 100644 --- a/yaml/cond.go +++ b/yaml/cond.go @@ -32,12 +32,15 @@ func (c *Condition) Match(v string) bool { if c.Excludes(v) { return false } + if c.Includes(v) { return true } + if len(c.Include) == 0 { return true } + return false } @@ -49,6 +52,7 @@ func (c *Condition) Includes(v string) bool { return true } } + return false } @@ -60,13 +64,17 @@ func (c *Condition) Excludes(v string) bool { return true } } + return false } // UnmarshalYAML implements yml unmarshalling. func (c *Condition) UnmarshalYAML(unmarshal func(interface{}) error) error { - var out1 string - var out2 []string + var ( + out1 string + out2 []string + ) + out3 := struct { Include []string Exclude []string @@ -75,6 +83,7 @@ func (c *Condition) UnmarshalYAML(unmarshal func(interface{}) error) error { err := unmarshal(&out1) if err == nil { c.Include = []string{out1} + return nil } diff --git a/yaml/cron.go b/yaml/cron.go index 52e3ee8..9fe1cb9 100644 --- a/yaml/cron.go +++ b/yaml/cron.go @@ -5,6 +5,8 @@ package yaml import "errors" +var ErrInvlaidCronBranch = errors.New("yaml: invalid cron branch") + type ( // Cron is a resource that defines a cron job, used // to execute pipelines at scheduled intervals. @@ -19,9 +21,9 @@ type ( // CronSpec defines the cron job. CronSpec struct { - Schedule string `json:"schedule,omitempty"` - Branch string `json:"branch,omitempty"` - Deploy CronDeployment `json:"deployment,omitempty" yaml:"deployment"` + Schedule string `json:"schedule,omitempty"` + Branch string `json:"branch,omitempty"` + Deployment CronDeployment `json:"deployment,omitempty" yaml:"deployment"` } // CronDeployment defines a cron job deployment. @@ -40,7 +42,7 @@ func (c *Cron) GetKind() string { return c.Kind } func (c Cron) Validate() error { switch { case c.Spec.Branch == "": - return errors.New("yaml: invalid cron branch") + return ErrInvlaidCronBranch default: return nil } diff --git a/yaml/env.go b/yaml/env.go index 70d13fd..263a712 100644 --- a/yaml/env.go +++ b/yaml/env.go @@ -8,39 +8,46 @@ type ( // can be defined as a string literal or as a reference // to a secret. Variable struct { - Value string `json:"value,omitempty"` - Secret string `json:"from_secret,omitempty" yaml:"from_secret"` + Value string `json:"value,omitempty"` + FromSecret string `json:"from_secret,omitempty" yaml:"from_secret"` } // variable is a tempoary type used to unmarshal // variables with references to secrets. variable struct { - Value string - Secret string `yaml:"from_secret"` + Value string + FromSecret string `yaml:"from_secret"` } ) // UnmarshalYAML implements yaml unmarshalling. func (v *Variable) UnmarshalYAML(unmarshal func(interface{}) error) error { d := new(variable) + err := unmarshal(&d.Value) if err != nil { err = unmarshal(d) } + v.Value = d.Value - v.Secret = d.Secret + v.FromSecret = d.FromSecret + return err } // MarshalYAML implements yaml marshalling. func (v *Variable) MarshalYAML() (interface{}, error) { - if v.Secret != "" { + if v.FromSecret != "" { m := map[string]interface{}{} - m["from_secret"] = v.Secret + m["from_secret"] = v.FromSecret + return m, nil } + if v.Value != "" { return v.Value, nil } + + //nolint:nilnil return nil, nil } diff --git a/yaml/linter/config.go b/yaml/linter/config.go index e104377..936d989 100644 --- a/yaml/linter/config.go +++ b/yaml/linter/config.go @@ -28,11 +28,12 @@ var ErrPipelineSelfDependency = errors.New("linter: pipeline cannot have a depen // Manifest performs lint operations for a manifest. func Manifest(manifest *yaml.Manifest, trusted bool) error { - return checkPipelines(manifest, trusted) + return checkPipelines(manifest) } -func checkPipelines(manifest *yaml.Manifest, trusted bool) error { +func checkPipelines(manifest *yaml.Manifest) error { names := map[string]struct{}{} + for _, resource := range manifest.Resources { switch v := resource.(type) { case *yaml.Pipeline: @@ -40,11 +41,14 @@ func checkPipelines(manifest *yaml.Manifest, trusted bool) error { if ok { return ErrDuplicatePipelineName } + names[v.Name] = struct{}{} + err := checkPipelineDeps(v, names) if err != nil { return err } + if (v.Kind == "pipeline" || v.Kind == "") && (v.Type == "" || v.Type == "docker") { err = checkPlatform(v.Platform) if err != nil { @@ -55,6 +59,7 @@ func checkPipelines(manifest *yaml.Manifest, trusted bool) error { continue } } + return nil } @@ -64,9 +69,11 @@ func checkPipelineDeps(pipeline *yaml.Pipeline, deps map[string]struct{}) error if !ok { return ErrMissingPipelineDependency } + if pipeline.Name == dep { return ErrPipelineSelfDependency } } + return nil } diff --git a/yaml/linter/linter.go b/yaml/linter/linter.go index 5970f2f..e2a43a5 100644 --- a/yaml/linter/linter.go +++ b/yaml/linter/linter.go @@ -10,29 +10,49 @@ import ( "github.com/drone/drone-yaml/yaml" ) -var os = map[string]struct{}{ - "linux": {}, - "windows": {}, -} +//nolint:gochecknoglobals +var ( + os = map[string]struct{}{ + "linux": {}, + "windows": {}, + } + arch = map[string]struct{}{ + "arm": {}, + "arm64": {}, + "amd64": {}, + } +) -var arch = map[string]struct{}{ - "arm": {}, - "arm64": {}, - "amd64": {}, -} +var ( + // ErrDuplicateStepName is returned when two Pipeline steps + // have the same name. + ErrDuplicateStepName = errors.New("linter: duplicate step names") -// ErrDuplicateStepName is returned when two Pipeline steps -// have the same name. -var ErrDuplicateStepName = errors.New("linter: duplicate step names") + // ErrMissingDependency is returned when a Pipeline step + // defines dependencies that are invalid or unknown. + ErrMissingDependency = errors.New("linter: invalid or unknown step dependency") -// ErrMissingDependency is returned when a Pipeline step -// defines dependencies that are invalid or unknown. -var ErrMissingDependency = errors.New("linter: invalid or unknown step dependency") + // ErrCyclicalDependency is returned when a Pipeline step + // defines a cyclical dependency, which would result in an + // infinite execution loop. + ErrCyclicalDependency = errors.New("linter: cyclical step dependency detected") -// ErrCyclicalDependency is returned when a Pipeline step -// defines a cyclical dependency, which would result in an -// infinite execution loop. -var ErrCyclicalDependency = errors.New("linter: cyclical step dependency detected") + ErrUnsupportedOS = errors.New("linter: unsupported os") + ErrUnsupportedArch = errors.New("linter: unsupported architecture") + ErrInvalidImage = errors.New("linter: invalid or missing image") + ErrInvalidBuildImage = errors.New("linter: invalid or missing build image") + ErrInvalidName = errors.New("linter: invalid or missing name") + ErrPrivilegedNotAllowed = errors.New("linter: untrusted repositories cannot enable privileged mode") + ErrMountNotAllowed = errors.New("linter: untrusted repositories cannot mount devices") + ErrDNSNotAllowed = errors.New("linter: untrusted repositories cannot configure dns") + ErrDNSSearchNotAllowed = errors.New("linter: untrusted repositories cannot configure dns_search") + ErrExtraHostsNotAllowed = errors.New("linter: untrusted repositories cannot configure extra_hosts") + ErrNetworkModeNotAllowed = errors.New("linter: untrusted repositories cannot configure network_mode") + ErrInvalidVolumeName = errors.New("linter: invalid volume name") + ErrHostPortNotAllowed = errors.New("linter: untrusted repositories cannot map to a host port") + ErrHostVolumeNotAllowed = errors.New("linter: untrusted repositories cannot mount host volumes") + ErrTempVolumeNotAllowed = errors.New("linter: untrusted repositories cannot mount in-memory volumes") +) // Lint performs lint operations for a resource. func Lint(resource yaml.Resource, trusted bool) error { @@ -57,19 +77,23 @@ func checkPipeline(pipeline *yaml.Pipeline, trusted bool) error { if err != nil { return err } + err = checkPlatform(pipeline.Platform) if err != nil { return err } + names := map[string]struct{}{} if !pipeline.Clone.Disable { names["clone"] = struct{}{} } + for _, container := range pipeline.Steps { _, ok := names[container.Name] if ok { return ErrDuplicateStepName } + names[container.Name] = struct{}{} err := checkContainer(container, trusted) @@ -82,11 +106,13 @@ func checkPipeline(pipeline *yaml.Pipeline, trusted bool) error { return err } } + for _, container := range pipeline.Services { _, ok := names[container.Name] if ok { return ErrDuplicateStepName } + names[container.Name] = struct{}{} err := checkContainer(container, trusted) @@ -94,6 +120,7 @@ func checkPipeline(pipeline *yaml.Pipeline, trusted bool) error { return err } } + return nil } @@ -101,15 +128,17 @@ func checkPlatform(platform yaml.Platform) error { if v := platform.OS; v != "" { _, ok := os[v] if !ok { - return fmt.Errorf("linter: unsupported os: %s", v) + return fmt.Errorf("%w: %s", ErrUnsupportedOS, v) } } + if v := platform.Arch; v != "" { _, ok := arch[v] if !ok { - return fmt.Errorf("linter: unsupported architecture: %s", v) + return fmt.Errorf("%w: %s", ErrUnsupportedArch, v) } } + return nil } @@ -118,39 +147,50 @@ func checkContainer(container *yaml.Container, trusted bool) error { if err != nil { return err } + if container.Build == nil && container.Image == "" { - return errors.New("linter: invalid or missing image") + return ErrInvalidImage } + if container.Build != nil && container.Build.Image == "" { - return errors.New("linter: invalid or missing build image") + return ErrInvalidBuildImage } + if container.Name == "" { - return errors.New("linter: invalid or missing name") + return ErrInvalidName } + if trusted && container.Privileged { - return errors.New("linter: untrusted repositories cannot enable privileged mode") + return ErrPrivilegedNotAllowed } + if trusted && len(container.Devices) > 0 { - return errors.New("linter: untrusted repositories cannot mount devices") + return ErrMountNotAllowed } + if trusted && len(container.DNS) > 0 { - return errors.New("linter: untrusted repositories cannot configure dns") + return ErrDNSNotAllowed } + if trusted && len(container.DNSSearch) > 0 { - return errors.New("linter: untrusted repositories cannot configure dns_search") + return ErrDNSSearchNotAllowed } + if trusted && len(container.ExtraHosts) > 0 { - return errors.New("linter: untrusted repositories cannot configure extra_hosts") + return ErrExtraHostsNotAllowed } - if trusted && len(container.Network) > 0 { - return errors.New("linter: untrusted repositories cannot configure network_mode") + + if trusted && len(container.NetworkMode) > 0 { + return ErrNetworkModeNotAllowed } + for _, mount := range container.Volumes { switch mount.Name { case "workspace", "_workspace", "_docker_socket": - return fmt.Errorf("linter: invalid volume name: %s", mount.Name) + return fmt.Errorf("%w: %s", ErrInvalidVolumeName, mount.Name) } } + return nil } @@ -161,49 +201,56 @@ func checkPorts(ports []*yaml.Port, trusted bool) error { return err } } + return nil } func checkPort(port *yaml.Port, trusted bool) error { if trusted && port.Host != 0 { - return errors.New("linter: untrusted repositories cannot map to a host port") + return ErrHostPortNotAllowed } + return nil } func checkVolumes(pipeline *yaml.Pipeline, trusted bool) error { for _, volume := range pipeline.Volumes { - if volume.EmptyDir != nil { - err := checkEmptyDirVolume(volume.EmptyDir, trusted) + if volume.Temp != nil { + err := checkEmptyDirVolume(volume.Temp, trusted) if err != nil { return err } } - if volume.HostPath != nil { - err := checkHostPathVolume(volume.HostPath, trusted) + + if volume.Host != nil { + err := checkHostPathVolume(trusted) if err != nil { return err } } + switch volume.Name { case "workspace", "_workspace", "_docker_socket": - return fmt.Errorf("linter: invalid volume name: %s", volume.Name) + return fmt.Errorf("%w: %s", ErrInvalidVolumeName, volume.Name) } } + return nil } -func checkHostPathVolume(volume *yaml.VolumeHostPath, trusted bool) error { +func checkHostPathVolume(trusted bool) error { if trusted { - return errors.New("linter: untrusted repositories cannot mount host volumes") + return ErrHostVolumeNotAllowed } + return nil } func checkEmptyDirVolume(volume *yaml.VolumeEmptyDir, trusted bool) error { if trusted && volume.Medium == "memory" { - return errors.New("linter: untrusted repositories cannot mount in-memory volumes") + return ErrTempVolumeNotAllowed } + return nil } @@ -213,9 +260,11 @@ func checkDeps(container *yaml.Container, deps map[string]struct{}) error { if !ok { return ErrMissingDependency } + if container.Name == dep { return ErrCyclicalDependency } } + return nil } diff --git a/yaml/manifest.go b/yaml/manifest.go index b1ddacb..992e79c 100644 --- a/yaml/manifest.go +++ b/yaml/manifest.go @@ -20,6 +20,8 @@ const ( KindSignature = "signature" ) +var ErrMarshalNotImplemented = errors.New("yaml: marshal not implemented") + type ( // Manifest is a collection of Drone resources. Manifest struct { @@ -44,6 +46,7 @@ type ( Data []byte `yaml:"-"` } + //nolint:musttag resource struct { Version string Kind string `json:"kind"` @@ -54,17 +57,22 @@ type ( // UnmarshalJSON implement the json.Unmarshaler. func (m *Manifest) UnmarshalJSON(b []byte) error { messages := []json.RawMessage{} + err := json.Unmarshal(b, &messages) if err != nil { return err } + for _, message := range messages { res := new(resource) + err := json.Unmarshal(message, res) if err != nil { return err } + var obj Resource + switch res.Kind { case "cron": obj = new(Cron) @@ -77,12 +85,15 @@ func (m *Manifest) UnmarshalJSON(b []byte) error { default: obj = new(Pipeline) } + err = json.Unmarshal(message, obj) if err != nil { return err } + m.Resources = append(m.Resources, obj) } + return nil } @@ -96,17 +107,19 @@ func (m *Manifest) MarshalJSON() ([]byte, error) { // documents, and MarshalYAML would otherwise attempt to marshal // as a single Yaml document. Use the Encode method instead. func (m *Manifest) MarshalYAML() (interface{}, error) { - return nil, errors.New("yaml: marshal not implemented") + return nil, ErrMarshalNotImplemented } // Encode encodes the manifest in Yaml format. func (m *Manifest) Encode() ([]byte, error) { buf := new(bytes.Buffer) enc := yaml.NewEncoder(buf) + for _, res := range m.Resources { if err := enc.Encode(res); err != nil { return nil, err } } + return buf.Bytes(), nil } diff --git a/yaml/param.go b/yaml/param.go index 0249743..375bb3c 100644 --- a/yaml/param.go +++ b/yaml/param.go @@ -8,14 +8,14 @@ type ( // can be defined as a literal or as a reference // to a secret. Parameter struct { - Value interface{} `json:"value,omitempty"` - Secret string `json:"from_secret,omitempty" yaml:"from_secret"` + Value interface{} `json:"value,omitempty"` + FromSecret string `json:"from_secret,omitempty" yaml:"from_secret"` } // parameter is a tempoary type used to unmarshal // parameters with references to secrets. parameter struct { - Secret string `yaml:"from_secret"` + FromSecret string `yaml:"from_secret"` } ) @@ -23,25 +23,33 @@ type ( func (p *Parameter) UnmarshalYAML(unmarshal func(interface{}) error) error { d := new(parameter) err := unmarshal(d) - if err == nil && d.Secret != "" { - p.Secret = d.Secret + + if err == nil && d.FromSecret != "" { + p.FromSecret = d.FromSecret + return nil } + var i interface{} err = unmarshal(&i) p.Value = i + return err } // MarshalYAML implements yaml marshalling. func (p *Parameter) MarshalYAML() (interface{}, error) { - if p.Secret != "" { + if p.FromSecret != "" { m := map[string]interface{}{} - m["from_secret"] = p.Secret + m["from_secret"] = p.FromSecret + return m, nil } + if p.Value != "" { return p.Value, nil } + + //nolint:nilnil return nil, nil } diff --git a/yaml/parse.go b/yaml/parse.go index c2bf3b8..ae97544 100644 --- a/yaml/parse.go +++ b/yaml/parse.go @@ -14,7 +14,7 @@ import ( "gopkg.in/yaml.v2" ) -var errorMissingKind = errors.New("yaml: missing kind attribute") +var ErrMissingKind = errors.New("yaml: missing kind attribute") // Parse parses the configuration from io.Reader r. func Parse(r io.Reader) (*Manifest, error) { @@ -22,23 +22,29 @@ func Parse(r io.Reader) (*Manifest, error) { if err != nil { return nil, err } + manifest := new(Manifest) + for _, raw := range resources { if raw == nil { continue } + resource, err := parseRaw(raw) if err != nil { return nil, err } + if resource.GetKind() == "" { - return nil, errorMissingKind + return nil, ErrMissingKind } + manifest.Resources = append( manifest.Resources, resource, ) } + return manifest, nil } @@ -63,11 +69,13 @@ func ParseFile(p string) (*Manifest, error) { return nil, err } defer f.Close() + return Parse(f) } -func parseRaw(r *RawResource) (Resource, error) { +func parseRaw(r *RawResource) (Resource, error) { //nolint:ireturn var obj Resource + switch r.Kind { case "cron": obj = new(Cron) @@ -80,7 +88,9 @@ func parseRaw(r *RawResource) (Resource, error) { default: obj = new(Pipeline) } + err := yaml.Unmarshal(r.Data, obj) + return obj, err } @@ -88,8 +98,11 @@ func parseRaw(r *RawResource) (Resource, error) { // io.Reader and returns a slice of raw resources. func ParseRaw(r io.Reader) ([]*RawResource, error) { const newline = '\n' - var resources []*RawResource - var resource *RawResource + + var ( + resources []*RawResource + resource *RawResource + ) scanner := bufio.NewScanner(r) for scanner.Scan() { @@ -97,34 +110,42 @@ func ParseRaw(r io.Reader) ([]*RawResource, error) { if isSeparator(line) { resource = nil } + if resource == nil { resource = &RawResource{} resources = append(resources, resource) } + if isSeparator(line) { continue } + if isTerminator(line) { break } - if scanner.Err() == io.EOF { + + if errors.Is(scanner.Err(), io.EOF) { break } + resource.Data = append( resource.Data, line..., ) + resource.Data = append( resource.Data, newline, ) } + for _, resource := range resources { err := yaml.Unmarshal(resource.Data, resource) if err != nil { return nil, err } } + return resources, nil } @@ -152,6 +173,7 @@ func ParseRawFile(p string) ([]*RawResource, error) { return nil, err } defer f.Close() + return ParseRaw(f) } diff --git a/yaml/pipeline.go b/yaml/pipeline.go index a93837f..5aaca32 100644 --- a/yaml/pipeline.go +++ b/yaml/pipeline.go @@ -11,17 +11,17 @@ type Pipeline struct { Type string `json:"type,omitempty"` Name string `json:"name,omitempty"` - Clone Clone `json:"clone,omitempty"` - Concurrency Concurrency `json:"concurrency,omitempty"` - DependsOn []string `json:"depends_on,omitempty" yaml:"depends_on" ` - Node map[string]string `json:"node,omitempty" yaml:"node"` - Platform Platform `json:"platform,omitempty"` - PullSecrets []string `json:"image_pull_secrets,omitempty" yaml:"image_pull_secrets"` - Services []*Container `json:"services,omitempty"` - Steps []*Container `json:"steps,omitempty"` - Trigger Conditions `json:"trigger,omitempty"` - Volumes []*Volume `json:"volumes,omitempty"` - Workspace Workspace `json:"workspace,omitempty"` + Clone Clone `json:"clone,omitempty"` + Concurrency Concurrency `json:"concurrency,omitempty"` + DependsOn []string `json:"depends_on,omitempty" yaml:"depends_on" ` + Node map[string]string `json:"node,omitempty" yaml:"node"` + Platform Platform `json:"platform,omitempty"` + ImagePullSecrets []string `json:"image_pull_secrets,omitempty" yaml:"image_pull_secrets"` + Services []*Container `json:"services,omitempty"` + Steps []*Container `json:"steps,omitempty"` + Trigger Conditions `json:"trigger,omitempty"` + Volumes []*Volume `json:"volumes,omitempty"` + Workspace Workspace `json:"workspace,omitempty"` } // GetVersion returns the resource version. @@ -58,7 +58,7 @@ type ( ExtraHosts []string `json:"extra_hosts,omitempty" yaml:"extra_hosts"` Failure string `json:"failure,omitempty"` Image string `json:"image,omitempty"` - Network string `json:"network_mode,omitempty" yaml:"network_mode"` + NetworkMode string `json:"network_mode,omitempty" yaml:"network_mode"` Name string `json:"name,omitempty"` Ports []*Port `json:"ports,omitempty"` Privileged bool `json:"privileged,omitempty"` @@ -102,23 +102,23 @@ type ( // Volume that can be mounted by containers. Volume struct { - Name string `json:"name,omitempty"` - EmptyDir *VolumeEmptyDir `json:"temp,omitempty" yaml:"temp"` - HostPath *VolumeHostPath `json:"host,omitempty" yaml:"host"` + Name string `json:"name,omitempty"` + Temp *VolumeEmptyDir `json:"temp,omitempty" yaml:"temp"` + Host *VolumeHostPath `json:"host,omitempty" yaml:"host"` } // VolumeDevice describes a mapping of a raw block // device within a container. VolumeDevice struct { - Name string `json:"name,omitempty"` - DevicePath string `json:"path,omitempty" yaml:"path"` + Name string `json:"name,omitempty"` + Path string `json:"path,omitempty" yaml:"path"` } // VolumeMount describes a mounting of a Volume // within a container. VolumeMount struct { - Name string `json:"name,omitempty"` - MountPath string `json:"path,omitempty" yaml:"path"` + Name string `json:"name,omitempty"` + Path string `json:"path,omitempty" yaml:"path"` } // VolumeEmptyDir mounts a temporary directory from the diff --git a/yaml/port.go b/yaml/port.go index 1168cd6..e077dde 100644 --- a/yaml/port.go +++ b/yaml/port.go @@ -22,12 +22,15 @@ type ( // UnmarshalYAML implements yaml unmarshalling. func (p *Port) UnmarshalYAML(unmarshal func(interface{}) error) error { out := new(port) + err := unmarshal(&out.Port) if err != nil { err = unmarshal(&out) } + p.Port = out.Port p.Host = out.Host p.Protocol = out.Protocol + return err } diff --git a/yaml/pretty/container.go b/yaml/pretty/container.go index a568293..a5b59be 100644 --- a/yaml/pretty/container.go +++ b/yaml/pretty/container.go @@ -19,6 +19,7 @@ func printContainer(w writer, v *yaml.Container) { if v.Build != nil { printBuild(w, v.Build) } + if v.Push != nil { w.WriteTagValue("push", v.Push.Image) } @@ -32,7 +33,7 @@ func printContainer(w writer, v *yaml.Container) { w.WriteTagValue("dns", v.DNS) w.WriteTagValue("dns_search", v.DNSSearch) w.WriteTagValue("extra_hosts", v.ExtraHosts) - w.WriteTagValue("network_mode", v.Network) + w.WriteTagValue("network_mode", v.NetworkMode) if len(v.Settings) > 0 { printSettings(w, v.Settings) @@ -49,21 +50,27 @@ func printContainer(w writer, v *yaml.Container) { if len(v.Devices) > 0 { printDeviceMounts(w, v.Devices) } + if len(v.Ports) > 0 { printPorts(w, v.Ports) } + if v.Resources != nil { printResources(w, v.Resources) } + if len(v.Volumes) > 0 { printVolumeMounts(w, v.Volumes) } + if !isConditionsEmpty(v.When) { printConditions(w, "when", v.When) } + if len(v.DependsOn) > 0 { printDependsOn(w, v.DependsOn) } + _ = w.WriteByte('\n') w.IndentDecrease() } @@ -93,43 +100,49 @@ func printDependsOn(w writer, v []string) { // helper function pretty prints the device sequence. func printDeviceMounts(w writer, v []*yaml.VolumeDevice) { w.WriteTag("devices") + for _, v := range v { s := new(indexWriter) s.writer = w s.IndentIncrease() s.WriteTagValue("name", v.Name) - s.WriteTagValue("path", v.DevicePath) + s.WriteTagValue("path", v.Path) s.IndentDecrease() } } // helper function pretty prints the environment mapping. func printEnviron(w writer, v map[string]*yaml.Variable) { - var keys []string + keys := make([]string, 0) + for k := range v { keys = append(keys, k) } + sort.Strings(keys) w.WriteTag("environment") w.IndentIncrease() + for _, k := range keys { v := v[k] - if v.Secret == "" { + if v.FromSecret == "" { w.WriteTagValue(k, v.Value) } else { w.WriteTag(k) w.IndentIncrease() - w.WriteTagValue("from_secret", v.Secret) + w.WriteTagValue("from_secret", v.FromSecret) w.IndentDecrease() } } + w.IndentDecrease() } // helper function pretty prints the port sequence. func printPorts(w writer, v []*yaml.Port) { w.WriteTag("ports") + for _, v := range v { if shortPort(v) { _ = w.WriteByte('\n') @@ -137,6 +150,7 @@ func printPorts(w writer, v []*yaml.Port) { _ = w.WriteByte('-') _ = w.WriteByte(' ') writeInt(w, v.Port) + continue } @@ -162,6 +176,7 @@ func printResources(w writer, v *yaml.Resources) { w.WriteTagValue("memory", v.Limits.Memory) w.IndentDecrease() } + if v.Requests != nil { w.WriteTag("requests") w.IndentIncrease() @@ -169,38 +184,44 @@ func printResources(w writer, v *yaml.Resources) { w.WriteTagValue("memory", v.Requests.Memory) w.IndentDecrease() } + w.IndentDecrease() } // helper function pretty prints the resoure mapping. func printSettings(w writer, v map[string]*yaml.Parameter) { - var keys []string + keys := make([]string, 0) + for k := range v { keys = append(keys, k) } + sort.Strings(keys) w.WriteTag("settings") w.IndentIncrease() + for _, k := range keys { v := v[k] - if v.Secret == "" { + if v.FromSecret == "" { w.IncludeZero() w.WriteTagValue(k, v.Value) w.ExcludeZero() } else { w.WriteTag(k) w.IndentIncrease() - w.WriteTagValue("from_secret", v.Secret) + w.WriteTagValue("from_secret", v.FromSecret) w.IndentDecrease() } } + w.IndentDecrease() } // helper function pretty prints the volume sequence. func printVolumeMounts(w writer, v []*yaml.VolumeMount) { w.WriteTag("volumes") + for _, v := range v { s := new(indexWriter) s.writer = w @@ -208,7 +229,7 @@ func printVolumeMounts(w writer, v []*yaml.VolumeMount) { s.IndentIncrease() s.WriteTagValue("name", v.Name) - s.WriteTagValue("path", v.MountPath) + s.WriteTagValue("path", v.Path) s.IndentDecrease() w.IndentDecrease() diff --git a/yaml/pretty/cron.go b/yaml/pretty/cron.go index 5832b0e..c825c8d 100644 --- a/yaml/pretty/cron.go +++ b/yaml/pretty/cron.go @@ -23,9 +23,11 @@ func printSpec(w writer, v *yaml.Cron) { w.IndentIncrease() w.WriteTagValue("schedule", v.Spec.Schedule) w.WriteTagValue("branch", v.Spec.Branch) + if hasDeployment(v) { printDeploy(w, v) } + w.IndentDecrease() } @@ -33,12 +35,12 @@ func printSpec(w writer, v *yaml.Cron) { func printDeploy(w writer, v *yaml.Cron) { w.WriteTag("deployment") w.IndentIncrease() - w.WriteTagValue("target", v.Spec.Deploy.Target) + w.WriteTagValue("target", v.Spec.Deployment.Target) w.IndentDecrease() } // helper function returns true if the deployment // object is empty. func hasDeployment(v *yaml.Cron) bool { - return v.Spec.Deploy.Target != "" + return v.Spec.Deployment.Target != "" } diff --git a/yaml/pretty/pipeline.go b/yaml/pretty/pipeline.go index 679cdba..a600045 100644 --- a/yaml/pretty/pipeline.go +++ b/yaml/pretty/pipeline.go @@ -21,22 +21,27 @@ func printPipeline(w writer, v *yaml.Pipeline) { } else { printPlatformDefault(w) } + if !isCloneEmpty(v.Clone) { printClone(w, v.Clone) } + if !isConcurrencyEmpty(v.Concurrency) { printConcurrency(w, v.Concurrency) } + if !isWorkspaceEmpty(v.Workspace) { printWorkspace(w, v.Workspace) } if len(v.Steps) > 0 { w.WriteTag("steps") + for _, step := range v.Steps { if step == nil { continue } + seq := new(indexWriter) seq.writer = w seq.IndentIncrease() @@ -47,10 +52,12 @@ func printPipeline(w writer, v *yaml.Pipeline) { if len(v.Services) > 0 { w.WriteTag("services") + for _, step := range v.Services { if step == nil { continue } + seq := new(indexWriter) seq.writer = w seq.IndentIncrease() @@ -64,8 +71,8 @@ func printPipeline(w writer, v *yaml.Pipeline) { _ = w.WriteByte('\n') } - if len(v.PullSecrets) > 0 { - w.WriteTagValue("image_pull_secrets", v.PullSecrets) + if len(v.ImagePullSecrets) > 0 { + w.WriteTagValue("image_pull_secrets", v.ImagePullSecrets) _ = w.WriteByte('\n') } @@ -111,42 +118,54 @@ func printConcurrency(w writer, v yaml.Concurrency) { func printConditions(w writer, name string, v yaml.Conditions) { w.WriteTag(name) w.IndentIncrease() + if !isConditionEmpty(v.Action) { printCondition(w, "action", v.Action) } + if !isConditionEmpty(v.Branch) { printCondition(w, "branch", v.Branch) } + if !isConditionEmpty(v.Cron) { printCondition(w, "cron", v.Cron) } + if !isConditionEmpty(v.Event) { printCondition(w, "event", v.Event) } + if !isConditionEmpty(v.Instance) { printCondition(w, "instance", v.Instance) } + if !isConditionEmpty(v.Paths) { printCondition(w, "paths", v.Paths) } + if !isConditionEmpty(v.Ref) { printCondition(w, "ref", v.Ref) } + if !isConditionEmpty(v.Repo) { printCondition(w, "repo", v.Repo) } + if !isConditionEmpty(v.Status) { printCondition(w, "status", v.Status) } + if !isConditionEmpty(v.Target) { printCondition(w, "target", v.Target) } + w.IndentDecrease() } // helper function pretty prints a condition mapping. func printCondition(w writer, k string, v yaml.Condition) { w.WriteTag(k) + if len(v.Include) != 0 && len(v.Exclude) == 0 { _ = w.WriteByte('\n') w.IndentIncrease() @@ -154,11 +173,13 @@ func printCondition(w writer, k string, v yaml.Condition) { writeValue(w, v.Include) w.IndentDecrease() } + if len(v.Include) != 0 && len(v.Exclude) != 0 { w.IndentIncrease() w.WriteTagValue("include", v.Include) w.IndentDecrease() } + if len(v.Exclude) != 0 { w.IndentIncrease() w.WriteTagValue("exclude", v.Exclude) @@ -197,6 +218,7 @@ func printPlatformDefault(w writer) { // helper function pretty prints the volume sequence. func printVolumes(w writer, v []*yaml.Volume) { w.WriteTag("volumes") + for _, v := range v { s := new(indexWriter) s.writer = w @@ -204,8 +226,10 @@ func printVolumes(w writer, v []*yaml.Volume) { s.IndentIncrease() s.WriteTagValue("name", v.Name) - if v := v.EmptyDir; v != nil { + + if v := v.Temp; v != nil { s.WriteTag("temp") + if isEmptyDirEmpty(v) { _ = w.WriteByte(' ') _ = w.WriteByte('{') @@ -218,7 +242,7 @@ func printVolumes(w writer, v []*yaml.Volume) { } } - if v := v.HostPath; v != nil { + if v := v.Host; v != nil { s.WriteTag("host") s.IndentIncrease() s.WriteTagValue("path", v.Path) diff --git a/yaml/pretty/pretty.go b/yaml/pretty/pretty.go index 61c7669..0fc3dbe 100644 --- a/yaml/pretty/pretty.go +++ b/yaml/pretty/pretty.go @@ -12,6 +12,7 @@ import ( // Print pretty prints the manifest. func Print(w io.Writer, v *yaml.Manifest) { state := new(baseWriter) + for _, r := range v.Resources { switch t := r.(type) { case *yaml.Cron: @@ -24,6 +25,7 @@ func Print(w io.Writer, v *yaml.Manifest) { printPipeline(state, t) } } + state.WriteString("...") state.WriteByte('\n') _, _ = w.Write(state.Bytes()) diff --git a/yaml/pretty/secret.go b/yaml/pretty/secret.go index 78d774b..928c128 100644 --- a/yaml/pretty/secret.go +++ b/yaml/pretty/secret.go @@ -9,8 +9,6 @@ import ( "github.com/drone/drone-yaml/yaml" ) -// TODO consider "!!binary |" for secret value - // helper function to pretty prints the signature resource. func printSecret(w writer, v *yaml.Secret) { _, _ = w.WriteString("---") @@ -22,11 +20,13 @@ func printSecret(w writer, v *yaml.Secret) { w.WriteTagValue("name", v.Name) printData(w, v.Data) } + if !isSecretGetEmpty(v.Get) { w.WriteTagValue("name", v.Name) _ = w.WriteByte('\n') printGet(w, v.Get) } + _ = w.WriteByte('\n') _ = w.WriteByte('\n') } @@ -42,22 +42,24 @@ func printGet(w writer, v yaml.SecretGet) { } func printData(w writer, d string) { + spaceReplacer := strings.NewReplacer(" ", "", "\n", "") + w.WriteTag("data") _ = w.WriteByte(' ') _ = w.WriteByte('>') w.IndentIncrease() + d = spaceReplacer.Replace(d) + //nolint:gomnd for _, s := range chunk(d, 60) { _ = w.WriteByte('\n') w.Indent() _, _ = w.WriteString(s) } + w.IndentDecrease() } -// replace spaces and newlines. -var spaceReplacer = strings.NewReplacer(" ", "", "\n", "") - // helper function returns true if the secret get // object is empty. func isSecretGetEmpty(v yaml.SecretGet) bool { diff --git a/yaml/pretty/util.go b/yaml/pretty/util.go index 5252242..6a9f524 100644 --- a/yaml/pretty/util.go +++ b/yaml/pretty/util.go @@ -72,6 +72,7 @@ func isQuoted(s string) bool { } var r0, r1 byte + t := strings.TrimSpace(s) // if the trimmed string does not match the string, it @@ -84,6 +85,7 @@ func isQuoted(s string) bool { if len(t) > 0 { r0 = t[0] } + if len(t) > 1 { r1 = t[1] } @@ -103,6 +105,7 @@ func isQuoted(s string) bool { } var prev rune + for _, b := range s { switch { case isEscapeCode(b): @@ -112,6 +115,7 @@ func isQuoted(s string) bool { case b == '#' && prev == ' ': return true } + prev = b } @@ -124,13 +128,17 @@ func chunk(s string, chunkSize int) []string { if len(s) == 0 { return []string{s} } + var chunks []string + for i := 0; i < len(s); i += chunkSize { nn := i + chunkSize if nn > len(s) { nn = len(s) } + chunks = append(chunks, s[i:nn]) } + return chunks } diff --git a/yaml/pretty/util_test.go b/yaml/pretty/util_test.go index a5b27de..403a37b 100644 --- a/yaml/pretty/util_test.go +++ b/yaml/pretty/util_test.go @@ -71,6 +71,7 @@ func TestQuoted(t *testing.T) { writeEncode(buf, test.before) a := test.after b := buf.String() + if b != a { t.Errorf("Want %q, got %q", a, b) } @@ -78,16 +79,17 @@ func TestQuoted(t *testing.T) { } func TestChunk(t *testing.T) { + testChunk := []string{ + "ZDllMjFjZDg3Zjk0ZWFjZDRhMjdhMTA1ZDQ1OTVkYTA1ODBjMTk0ZWVlZjQyNmU4", + "N2RiNTIwZjg0NWQwYjcyYjE3MmFmZDIyYzg3NTQ1N2YyYzgxODhjYjJmNDhhOTFj", + "ZjdhMzA0YjEzYWFlMmYxMTIwMmEyM2Q1YjQ5Yjg2ZmMK", + } + s := strings.Join(testChunk, "") got, want := chunk(s, 64), testChunk + if diff := cmp.Diff(got, want); diff != "" { t.Errorf("Unexpected chunk value") t.Log(diff) } } - -var testChunk = []string{ - "ZDllMjFjZDg3Zjk0ZWFjZDRhMjdhMTA1ZDQ1OTVkYTA1ODBjMTk0ZWVlZjQyNmU4", - "N2RiNTIwZjg0NWQwYjcyYjE3MmFmZDIyYzg3NTQ1N2YyYzgxODhjYjJmNDhhOTFj", - "ZjdhMzA0YjEzYWFlMmYxMTIwMmEyM2Q1YjQ5Yjg2ZmMK", -} diff --git a/yaml/pretty/writer.go b/yaml/pretty/writer.go index 5afb819..eb52bc5 100644 --- a/yaml/pretty/writer.go +++ b/yaml/pretty/writer.go @@ -12,9 +12,6 @@ import ( "github.com/drone/drone-yaml/yaml" ) -// TODO rename WriteTag to WriteKey -// TODO rename WriteTagValue to WriteKeyValue - // ESCAPING: // // The string starts with a special character: @@ -102,7 +99,9 @@ func (w *baseWriter) WriteTagValue(k, v interface{}) { if isZero(v) && !w.zero { return } + w.WriteTag(k) + switch { case isPrimative(v): _ = w.WriteByte(' ') @@ -142,6 +141,7 @@ func (w *indexWriter) ExcludeZero() { func (w *indexWriter) WriteTag(v interface{}) { _ = w.WriteByte('\n') + if w.index == 0 { w.IndentDecrease() w.Indent() @@ -151,6 +151,7 @@ func (w *indexWriter) WriteTag(v interface{}) { } else { w.Indent() } + writeValue(w, v) _ = w.WriteByte(':') w.index++ @@ -160,7 +161,9 @@ func (w *indexWriter) WriteTagValue(k, v interface{}) { if isZero(v) && !w.zero { return } + w.WriteTag(k) + switch { case isPrimative(v): _ = w.WriteByte(' ') @@ -212,8 +215,10 @@ func writeEncode(w writer, v string) { if len(v) == 0 { _ = w.WriteByte('"') _ = w.WriteByte('"') + return } + if isQuoted(v) { fmt.Fprintf(w, "%q", v) } else { @@ -224,8 +229,10 @@ func writeEncode(w writer, v string) { func writeValue(w writer, v interface{}) { if v == nil { _ = w.WriteByte('~') + return } + switch v := v.(type) { case bool, int, int64, float64, string: writeScalar(w, v) @@ -261,13 +268,16 @@ func writeSequence(w writer, v []interface{}) { if len(v) == 0 { _ = w.WriteByte('[') _ = w.WriteByte(']') + return } + for i, v := range v { if i != 0 { _ = w.WriteByte('\n') w.Indent() } + _ = w.WriteByte('-') _ = w.WriteByte(' ') w.IndentIncrease() @@ -280,13 +290,16 @@ func writeSequenceStr(w writer, v []string) { if len(v) == 0 { _ = w.WriteByte('[') _ = w.WriteByte(']') + return } + for i, v := range v { if i != 0 { _ = w.WriteByte('\n') w.Indent() } + _ = w.WriteByte('-') _ = w.WriteByte(' ') writeEncode(w, v) @@ -297,22 +310,30 @@ func writeMapping(w writer, v map[interface{}]interface{}) { if len(v) == 0 { _ = w.WriteByte('{') _ = w.WriteByte('}') + return } - var keys []string + + keys := make([]string, 0) + for k := range v { s := fmt.Sprint(k) keys = append(keys, s) } + sort.Strings(keys) + for i, k := range keys { v := v[k] + if i != 0 { _ = w.WriteByte('\n') w.Indent() } + writeEncode(w, k) _ = w.WriteByte(':') + if v == nil || isPrimative(v) || isZero(v) { _ = w.WriteByte(' ') writeValue(w, v) @@ -335,19 +356,26 @@ func writeMappingStr(w writer, v map[string]string) { if len(v) == 0 { _ = w.WriteByte('{') _ = w.WriteByte('}') + return } - var keys []string + + keys := make([]string, 0) + for k := range v { keys = append(keys, k) } + sort.Strings(keys) + for i, k := range keys { v := v[k] + if i != 0 { _ = w.WriteByte('\n') w.Indent() } + writeEncode(w, k) _ = w.WriteByte(':') _ = w.WriteByte(' ') diff --git a/yaml/pretty/writer_test.go b/yaml/pretty/writer_test.go index 9c827b4..7d0858e 100644 --- a/yaml/pretty/writer_test.go +++ b/yaml/pretty/writer_test.go @@ -13,25 +13,7 @@ import ( // this unit tests pretty prints a complex yaml structure // to ensure we have common use cases covered. func TestWriteComplexValue(t *testing.T) { - block := map[interface{}]interface{}{} - err := yaml.Unmarshal([]byte(testComplexValue), &block) - if err != nil { - t.Error(err) - return - } - - b := new(baseWriter) - writeValue(b, block) - got, want := b.String(), strings.TrimSpace(testComplexValue) - if got != want { - t.Errorf("Unexpected block format") - println(got) - println("---") - println(want) - } -} - -var testComplexValue = ` + testComplexValue := ` a: b c: - d @@ -57,3 +39,24 @@ x: ~ z: "#y" zz: "\nz\n" "{z}": z` + + block := map[interface{}]interface{}{} + + err := yaml.Unmarshal([]byte(testComplexValue), &block) + if err != nil { + t.Error(err) + + return + } + + b := new(baseWriter) + writeValue(b, block) + + got, want := b.String(), strings.TrimSpace(testComplexValue) + if got != want { + t.Errorf("Unexpected block format") + println(got) + println("---") + println(want) + } +} diff --git a/yaml/push.go b/yaml/push.go index 907d6b2..f4a526f 100644 --- a/yaml/push.go +++ b/yaml/push.go @@ -19,10 +19,13 @@ type ( // UnmarshalYAML implements yaml unmarshalling. func (p *Push) UnmarshalYAML(unmarshal func(interface{}) error) error { d := new(push) + err := unmarshal(&d.Image) if err != nil { err = unmarshal(d) } + p.Image = d.Image + return err } diff --git a/yaml/registry.go b/yaml/registry.go index fc19ccf..3d9fa82 100644 --- a/yaml/registry.go +++ b/yaml/registry.go @@ -18,6 +18,8 @@ type ( } ) +var ErrInvalidRegistry = errors.New("yaml: invalid registry resource") + // GetVersion returns the resource version. func (r *Registry) GetVersion() string { return r.Version } @@ -27,7 +29,8 @@ func (r *Registry) GetKind() string { return r.Kind } // Validate returns an error if the registry is invalid. func (r *Registry) Validate() error { if len(r.Data) == 0 { - return errors.New("yaml: invalid registry resource") + return ErrInvalidRegistry } + return nil } diff --git a/yaml/secret.go b/yaml/secret.go index 3c19b82..0761774 100644 --- a/yaml/secret.go +++ b/yaml/secret.go @@ -5,9 +5,6 @@ package yaml import "errors" -// TODO(bradrydzewski) deprecate Secret -// TODO(bradrydzewski) deprecate ExternalData - type ( // Secret is a resource that provides encrypted data // and pointers to external data (i.e. from vault). @@ -38,6 +35,8 @@ type ( } ) +var ErrInvalidSecret = errors.New("yaml: invalid secret resource") + // GetVersion returns the resource version. func (s *Secret) GetVersion() string { return s.Version } @@ -47,7 +46,8 @@ func (s *Secret) GetKind() string { return s.Kind } // Validate returns an error if the secret is invalid. func (s *Secret) Validate() error { if len(s.Data) == 0 && len(s.Get.Path) == 0 && len(s.Get.Name) == 0 { - return errors.New("yaml: invalid secret resource") + return ErrInvalidSecret } + return nil } diff --git a/yaml/signature.go b/yaml/signature.go index aa2de09..f186300 100644 --- a/yaml/signature.go +++ b/yaml/signature.go @@ -17,6 +17,8 @@ type ( } ) +var ErrInvalidSignature = errors.New("yaml: invalid signature due to missing hash") + // GetVersion returns the resource version. func (s *Signature) GetVersion() string { return s.Version } @@ -26,7 +28,8 @@ func (s *Signature) GetKind() string { return s.Kind } // Validate returns an error if the signature is invalid. func (s Signature) Validate() error { if s.Hmac == "" { - return errors.New("yaml: invalid signature. missing hash") + return ErrInvalidSignature } + return nil } diff --git a/yaml/unit.go b/yaml/unit.go index 585a5db..4eb1d4f 100644 --- a/yaml/unit.go +++ b/yaml/unit.go @@ -17,6 +17,7 @@ func (b *BytesSize) UnmarshalYAML(unmarshal func(interface{}) error) error { var intType int64 if err := unmarshal(&intType); err == nil { *b = BytesSize(intType) + return nil } @@ -29,6 +30,7 @@ func (b *BytesSize) UnmarshalYAML(unmarshal func(interface{}) error) error { if err == nil { *b = BytesSize(intType) } + return err } diff --git a/yaml/unit_test.go b/yaml/unit_test.go index 6b86314..4233cc0 100644 --- a/yaml/unit_test.go +++ b/yaml/unit_test.go @@ -34,14 +34,18 @@ func TestBytesSize(t *testing.T) { for _, test := range tests { in := []byte(test.yaml) out := BytesSize(0) + err := yaml.Unmarshal(in, &out) if err != nil { t.Error(err) + return } + if got, want := int64(out), test.size; got != want { t.Errorf("Want byte size %d, got %d", want, got) } + if got, want := out.String(), test.text; got != want { t.Errorf("Want byte text %s, got %s", want, got) }