diff --git a/Gopkg.lock b/Gopkg.lock index d9e649c..44cde97 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -180,15 +180,14 @@ version = "v1.1.1" [[projects]] - digest = "1:9840201525a2e1ada0caf4089928e9d749cf8ec88bdb4ed8525211149610cc05" + digest = "1:534ad88d138058854fb0029442cb101231fca8f3a368db486f9d76fecd970456" name = "github.com/deislabs/cnab-go" packages = [ "bundle", "bundle/definition", ] pruneopts = "UT" - revision = "d0e47f49ea9a4ef8aedf1043b11ffa7506fc10d7" - version = "v0.2.0-beta1" + revision = "v0.4.0-beta1" [[projects]] digest = "1:44885d3782f36ff3010fb2ae55d72276b0717700d3b95e2ae22a08ce25bf60ae" @@ -223,16 +222,16 @@ version = "v19.03.1" [[projects]] - branch = "canonical" - digest = "1:526f7377ece51c6ce27239ddf9e2c0860ccfd2584643bdf3853145b196019a30" + digest = "1:51fef110ff737b08bebfa091471714db028ebcbeda6a3cad8ca4512b39b34581" name = "github.com/docker/cnab-to-oci" packages = [ "converter", + "relocation", "remotes", ] pruneopts = "UT" - revision = "b55c93a7d0efd6067861d6221c470a5af201bcac" - source = "github.com/radu-matei/cnab-to-oci" + revision = "c33e316902ce0e44adc6e14731c7241286d17d8e" + version = "v0.2.0-beta1" [[projects]] digest = "1:b375e031f9124a02189d37d1b0d63bb3d9ec241c6ae1ae5c6e25c74d166c3e4f" diff --git a/Gopkg.toml b/Gopkg.toml index afce770..eac9020 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -4,8 +4,7 @@ [[constraint]] name = "github.com/docker/cnab-to-oci" - source = "github.com/radu-matei/cnab-to-oci" - branch = "canonical" + version = "v0.2.0-beta1" [[constraint]] name = "github.com/in-toto/in-toto-golang" diff --git a/vendor/github.com/deislabs/cnab-go/bundle/bundle.go b/vendor/github.com/deislabs/cnab-go/bundle/bundle.go index 909e7c9..3c34499 100644 --- a/vendor/github.com/deislabs/cnab-go/bundle/bundle.go +++ b/vendor/github.com/deislabs/cnab-go/bundle/bundle.go @@ -16,23 +16,25 @@ import ( // Bundle is a CNAB metadata document type Bundle struct { - SchemaVersion string `json:"schemaVersion" mapstructure:"schemaVersion"` - Name string `json:"name" mapstructure:"name"` - Version string `json:"version" mapstructure:"version"` - Description string `json:"description" mapstructure:"description"` - Keywords []string `json:"keywords,omitempty" mapstructure:"keywords"` - Maintainers []Maintainer `json:"maintainers,omitempty" mapstructure:"maintainers"` - InvocationImages []InvocationImage `json:"invocationImages" mapstructure:"invocationImages"` - Images map[string]Image `json:"images,omitempty" mapstructure:"images"` - Actions map[string]Action `json:"actions,omitempty" mapstructure:"actions"` - Parameters *ParametersDefinition `json:"parameters,omitempty" mapstructure:"parameters"` - Credentials map[string]Credential `json:"credentials,omitempty" mapstructure:"credentials"` - Outputs *OutputsDefinition `json:"outputs,omitempty" mapstructure:"outputs"` - Definitions definition.Definitions `json:"definitions,omitempty" mapstructure:"definitions"` + SchemaVersion string `json:"schemaVersion" yaml:"schemaVersion"` + Name string `json:"name" yaml:"name"` + Version string `json:"version" yaml:"version"` + Description string `json:"description" yaml:"description"` + Keywords []string `json:"keywords,omitempty" yaml:"keywords,omitempty"` + Maintainers []Maintainer `json:"maintainers,omitempty" yaml:"maintainers,omitempty"` + InvocationImages []InvocationImage `json:"invocationImages" yaml:"invocationImages"` + Images map[string]Image `json:"images,omitempty" yaml:"images,omitempty"` + Actions map[string]Action `json:"actions,omitempty" yaml:"actions,omitempty"` + Parameters map[string]Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` + Credentials map[string]Credential `json:"credentials,omitempty" yaml:"credentials,omitempty"` + Outputs map[string]Output `json:"outputs,omitempty" yaml:"outputs,omitempty"` + Definitions definition.Definitions `json:"definitions,omitempty" yaml:"definitions,omitempty"` + License string `json:"license,omitempty" yaml:"license,omitempty"` + RequiredExtensions []string `json:"requiredExtensions,omitempty" yaml:"requiredExtensions,omitempty"` // Custom extension metadata is a named collection of auxiliary data whose // meaning is defined outside of the CNAB specification. - Custom map[string]interface{} `json:"custom,omitempty" mapstructure:"custom"` + Custom map[string]interface{} `json:"custom,omitempty" yaml:"custom,omitempty"` } //Unmarshal unmarshals a Bundle that was not signed. @@ -70,54 +72,49 @@ func (b Bundle) WriteTo(w io.Writer) (int64, error) { // LocationRef specifies a location within the invocation package type LocationRef struct { - Path string `json:"path" mapstructure:"path"` - Field string `json:"field" mapstructure:"field"` - MediaType string `json:"mediaType" mapstructure:"mediaType"` + Path string `json:"path" yaml:"path"` + Field string `json:"field" yaml:"field"` + MediaType string `json:"mediaType" yaml:"mediaType"` } // BaseImage contains fields shared across image types type BaseImage struct { - ImageType string `json:"imageType" mapstructure:"imageType"` - Image string `json:"image" mapstructure:"image"` - Digest string `json:"digest,omitempty" mapstructure:"digest"` - Size uint64 `json:"size,omitempty" mapstructure:"size"` - Labels map[string]string `json:"labels,omitempty" mapstructure:"labels"` - MediaType string `json:"mediaType,omitempty" mapstructure:"mediaType"` + ImageType string `json:"imageType" yaml:"imageType"` + Image string `json:"image" yaml:"image"` + Digest string `json:"contentDigest,omitempty" yaml:"contentDigest,omitempty"` + Size uint64 `json:"size,omitempty" yaml:"size,omitempty"` + Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` + MediaType string `json:"mediaType,omitempty" yaml:"mediaType,omitempty"` } // Image describes a container image in the bundle type Image struct { - BaseImage `mapstructure:",squash"` - Description string `json:"description" mapstructure:"description"` //TODO: change? see where it's being used? change to description? + BaseImage `yaml:",inline"` + Description string `json:"description" yaml:"description"` //TODO: change? see where it's being used? change to description? } // InvocationImage contains the image type and location for the installation of a bundle type InvocationImage struct { - BaseImage `mapstructure:",squash"` + BaseImage `yaml:",inline"` } -// Map that stores the relocated images -// The key is the Image in bundle.json and the value is the new Image -// from the relocated registry -type ImageRelocationMap map[string]string - // Location provides the location where a value should be written in // the invocation image. // // A location may be either a file (by path) or an environment variable. type Location struct { - Path string `json:"path,omitempty" mapstructure:"path"` - EnvironmentVariable string `json:"env,omitempty" mapstructure:"env"` + Path string `json:"path,omitempty" yaml:"path,omitempty"` + EnvironmentVariable string `json:"env,omitempty" yaml:"env,omitempty"` } // Maintainer describes a code maintainer of a bundle type Maintainer struct { // Name is a user name or organization name - Name string `json:"name" mapstructure:"name"` + Name string `json:"name" yaml:"name"` // Email is an optional email address to contact the named maintainer - Email string `json:"email,omitempty" mapstructure:"email"` + Email string `json:"email,omitempty" yaml:"email,omitempty"` // Url is an optional URL to an address for the named maintainer - URL string `json:"url,omitempty" mapstructure:"url"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` } // Action describes a custom (non-core) action. @@ -125,36 +122,24 @@ type Action struct { // Modifies indicates whether this action modifies the release. // // If it is possible that an action modify a release, this must be set to true. - Modifies bool `json:"modifies,omitempty" mapstructure:"modifies"` + Modifies bool `json:"modifies,omitempty" yaml:"modifies,omitempty"` // Stateless indicates that the action is purely informational, that credentials are not required, and that the runtime should not keep track of its invocation - Stateless bool `json:"stateless,omitempty" mapstructure:"stateless"` + Stateless bool `json:"stateless,omitempty" yaml:"stateless,omitempty"` // Description describes the action as a user-readable string - Description string `json:"description,omitempty" mapstructure:"description"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` } // ValuesOrDefaults returns parameter values or the default parameter values. An error is returned when the parameter value does not pass -// the schema validation, a required parameter is missing or an immutable parameter is set with a new value. -func ValuesOrDefaults(vals map[string]interface{}, currentVals map[string]interface{}, b *Bundle) (map[string]interface{}, error) { +// the schema validation or a required parameter is missing. +func ValuesOrDefaults(vals map[string]interface{}, b *Bundle) (map[string]interface{}, error) { res := map[string]interface{}{} - if b.Parameters == nil { - return res, nil - } - - requiredMap := map[string]struct{}{} - for _, key := range b.Parameters.Required { - requiredMap[key] = struct{}{} - } - - for name, def := range b.Parameters.Fields { - s, ok := b.Definitions[def.Definition] + for name, param := range b.Parameters { + s, ok := b.Definitions[param.Definition] if !ok { return res, fmt.Errorf("unable to find definition for %s", name) } if val, ok := vals[name]; ok { - if currentVal, ok := currentVals[name]; def.Immutable && ok && currentVal != val { - return res, fmt.Errorf("parameter %s is immutable and cannot be overridden with value %v", name, val) - } valErrs, err := s.Validate(val) if err != nil { return res, pkgErrors.Wrapf(err, "encountered an error validating parameter %s", name) @@ -168,7 +153,7 @@ func ValuesOrDefaults(vals map[string]interface{}, currentVals map[string]interf typedVal := s.CoerceValue(val) res[name] = typedVal continue - } else if _, ok := requiredMap[name]; ok { + } else if param.Required { return res, fmt.Errorf("parameter %q is required", name) } res[name] = s.Default @@ -191,6 +176,22 @@ func (b Bundle) Validate() error { return errors.New("'latest' is not a valid bundle version") } + reqExt := make(map[string]bool, len(b.RequiredExtensions)) + for _, requiredExtension := range b.RequiredExtensions { + // Verify the custom extension declared as required exists + if _, exists := b.Custom[requiredExtension]; !exists { + return fmt.Errorf("required extension '%s' is not defined in the Custom section of the bundle", requiredExtension) + } + + // Check for duplicate entries + if _, exists := reqExt[requiredExtension]; exists { + return fmt.Errorf("required extension '%s' is already declared", requiredExtension) + } + + // Populate map with required extension, for duplicate check above + reqExt[requiredExtension] = true + } + for _, img := range b.InvocationImages { err := img.Validate() if err != nil { diff --git a/vendor/github.com/deislabs/cnab-go/bundle/credentials.go b/vendor/github.com/deislabs/cnab-go/bundle/credentials.go index 3ca2698..d5c033c 100644 --- a/vendor/github.com/deislabs/cnab-go/bundle/credentials.go +++ b/vendor/github.com/deislabs/cnab-go/bundle/credentials.go @@ -2,7 +2,7 @@ package bundle // Credential represents the definition of a CNAB credential type Credential struct { - Location `mapstructure:",squash"` - Description string `json:"description,omitempty" mapstructure:"description"` - Required bool `json:"required,omitempty" mapstructure:"required"` + Location `yaml:",inline"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Required bool `json:"required,omitempty" yaml:"required,omitempty"` } diff --git a/vendor/github.com/deislabs/cnab-go/bundle/definition/schema.go b/vendor/github.com/deislabs/cnab-go/bundle/definition/schema.go index 19abbb0..e756cc3 100644 --- a/vendor/github.com/deislabs/cnab-go/bundle/definition/schema.go +++ b/vendor/github.com/deislabs/cnab-go/bundle/definition/schema.go @@ -13,50 +13,51 @@ type Definitions map[string]*Schema // Schema represents a JSON Schema compatible CNAB Definition type Schema struct { - Comment string `json:"$comment,omitempty" mapstructure:"$comment,omitempty"` - ID string `json:"$id,omitempty" mapstructure:"$ref,omitempty"` - Ref string `json:"$ref,omitempty" mapstructure:"$ref,omitempty"` - AdditionalItems interface{} `json:"additionalItems,omitempty" mapstructure:"additionalProperties,omitempty"` - AdditionalProperties interface{} `json:"additionalProperties,omitempty" mapstructure:"additionalProperties,omitempty"` - AllOf []*Schema `json:"allOf,omitempty" mapstructure:"allOf,omitempty"` - Const interface{} `json:"const,omitempty" mapstructure:"const,omitempty"` - Contains *Schema `json:"contains,omitempty" mapstructure:"contains,omitempty"` - ContentEncoding string `json:"contentEncoding,omitempty" mapstructure:"contentEncoding,omitempty"` - ContentMediaType string `json:"contentMediaType,omitempty" mapstructure:"contentMediaType,omitempty"` - Default interface{} `json:"default,omitempty" mapstructure:"default,omitempty"` - Definitions Definitions `json:"definitions,omitempty" mapstructure:"definitions,omitempty"` - Dependencies map[string]interface{} `json:"dependencies,omitempty" mapstructure:"dependencies,omitempty"` - Description string `json:"description,omitempty" mapstructure:"description,omitempty"` - Else *Schema `json:"else,omitempty" mapstructure:"else,omitempty"` - Enum []interface{} `json:"enum,omitempty" mapstructure:"enum,omitempty"` - Examples []interface{} `json:"examples,omitempty" mapstructure:"examples,omitempty"` - ExclusiveMaximum *float64 `json:"exclusiveMaximum,omitempty" mapstructure:"exclusiveMaximum,omitempty"` - ExclusiveMinimum *float64 `json:"exclusiveMinimum,omitempty" mapstructure:"exclusiveMinimum,omitempty"` - Format string `json:"format,omitempty" mapstructure:"format,omitempty"` - If *Schema `json:"if,omitempty" mapstructure:"if,omitempty"` + Schema string `json:"$schema,omitempty" yaml:"$schema,omitempty"` + Comment string `json:"$comment,omitempty" yaml:"$comment,omitempty"` + ID string `json:"$id,omitempty" yaml:"$id,omitempty"` + Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + AdditionalItems interface{} `json:"additionalItems,omitempty" yaml:"additionalItems,omitempty"` + AdditionalProperties interface{} `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"` + AllOf []*Schema `json:"allOf,omitempty" yaml:"allOf,omitempty"` + Const interface{} `json:"const,omitempty" yaml:"const,omitempty"` + Contains *Schema `json:"contains,omitempty" yaml:"contains,omitempty"` + ContentEncoding string `json:"contentEncoding,omitempty" yaml:"contentEncoding,omitempty"` + ContentMediaType string `json:"contentMediaType,omitempty" yaml:"contentMediaType,omitempty"` + Default interface{} `json:"default,omitempty" yaml:"default,omitempty"` + Definitions Definitions `json:"definitions,omitempty" yaml:"definitions,omitempty"` + Dependencies map[string]interface{} `json:"dependencies,omitempty" yaml:"dependencies,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Else *Schema `json:"else,omitempty" yaml:"else,omitempty"` + Enum []interface{} `json:"enum,omitempty" yaml:"enum,omitempty"` + Examples []interface{} `json:"examples,omitempty" yaml:"examples,omitempty"` + ExclusiveMaximum *int `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"` + ExclusiveMinimum *int `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"` + Format string `json:"format,omitempty" yaml:"format,omitempty"` + If *Schema `json:"if,omitempty" yaml:"if,omitempty"` //Items can be a Schema or an Array of Schema :( - Items interface{} `json:"items,omitempty" mapstructure:"items,omitempty"` - Maximum *float64 `json:"maximum,omitempty" mapstructure:"maximum,omitempty"` - MaxLength *float64 `json:"maxLength,omitempty" mapstructure:"maxLength,omitempty"` - MinItems *float64 `json:"minItems,omitempty" mapstructure:"minItems,omitempty"` - MinLength *float64 `json:"minLength,omitempty" mapstructure:"minLength,omitempty"` - MinProperties *float64 `json:"minProperties,omitempty" mapstructure:"minProperties,omitempty"` - Minimum *float64 `json:"minimum,omitempty" mapstructure:"minimum,omitempty"` - MultipleOf *float64 `json:"multipleOf,omitempty" mapstructure:"multipleOf,omitempty"` - Not *Schema `json:"not,omitempty" mapstructure:"not,omitempty"` - OneOf *Schema `json:"oneOf,omitempty" mapstructure:"oneOf,omitempty"` + Items interface{} `json:"items,omitempty" yaml:"items,omitempty"` + Maximum *int `json:"maximum,omitempty" yaml:"maximum,omitempty"` + MaxLength *int `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` + MinItems *int `json:"minItems,omitempty" yaml:"minItems,omitempty"` + MinLength *int `json:"minLength,omitempty" yaml:"minLength,omitempty"` + MinProperties *int `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` + Minimum *int `json:"minimum,omitempty" yaml:"minimum,omitempty"` + MultipleOf *int `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` + Not *Schema `json:"not,omitempty" yaml:"not,omitempty"` + OneOf *Schema `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` - PatternProperties map[string]*Schema `json:"patternProperties,omitempty" mapstructure:"patternProperties,omitempty"` + PatternProperties map[string]*Schema `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"` - Properties map[string]*Schema `json:"properties,omitempty" mapstructure:"properties,omitempty"` - PropertyNames *Schema `json:"propertyNames,omitempty" mapstructure:"propertyNames,omitempty"` - ReadOnly *bool `json:"readOnly,omitempty" mapstructure:"readOnly,omitempty"` - Required []string `json:"required,omitempty" mapstructure:"required,omitempty"` - Then *Schema `json:"then,omitempty" mapstructure:"then,omitempty"` - Title string `json:"title,omitempty" mapstructure:"title,omitempty"` - Type interface{} `json:"type,omitempty" mapstructure:"type,omitempty"` - UniqueItems *bool `json:"uniqueItems,omitempty" mapstructure:"uniqueItems,omitempty"` - WriteOnly *bool `json:"writeOnly,omitempty" mapstructure:"writeOnly,omitempty"` + Properties map[string]*Schema `json:"properties,omitempty" yaml:"properties,omitempty"` + PropertyNames *Schema `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"` + ReadOnly *bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` + Required []string `json:"required,omitempty" yaml:"required,omitempty"` + Then *Schema `json:"then,omitempty" yaml:"then,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Type interface{} `json:"type,omitempty" yaml:"type,omitempty"` + UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` + WriteOnly *bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` } // GetType will return the singular type for a given schema and a success boolean. If the @@ -120,12 +121,6 @@ func (s *Schema) ConvertValue(val string) (interface{}, error) { switch dataType { case "string": return val, nil - case "number": - num, err := strconv.ParseFloat(val, 64) - if err != nil { - return nil, errors.Wrapf(err, "unable to convert %s to number", val) - } - return num, nil case "integer": return strconv.Atoi(val) case "boolean": diff --git a/vendor/github.com/deislabs/cnab-go/bundle/outputs.go b/vendor/github.com/deislabs/cnab-go/bundle/outputs.go index 00ef9f7..a17cfa8 100644 --- a/vendor/github.com/deislabs/cnab-go/bundle/outputs.go +++ b/vendor/github.com/deislabs/cnab-go/bundle/outputs.go @@ -1,12 +1,8 @@ package bundle -type OutputsDefinition struct { - Fields map[string]OutputDefinition `json:"fields" mapstructure:"fields"` -} - -type OutputDefinition struct { - Definition string `json:"definition" mapstructure:"definition"` - ApplyTo []string `json:"applyTo,omitempty" mapstructure:"applyTo,omitempty"` - Description string `json:"description,omitempty" mapstructure:"description"` - Path string `json:"path,omitemtpty" mapstructure:"path,omitempty"` +type Output struct { + Definition string `json:"definition" yaml:"definition"` + ApplyTo []string `json:"applyTo,omitempty" yaml:"applyTo,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Path string `json:"path" yaml:"path"` } diff --git a/vendor/github.com/deislabs/cnab-go/bundle/parameters.go b/vendor/github.com/deislabs/cnab-go/bundle/parameters.go index da959f9..c461c57 100644 --- a/vendor/github.com/deislabs/cnab-go/bundle/parameters.go +++ b/vendor/github.com/deislabs/cnab-go/bundle/parameters.go @@ -1,15 +1,10 @@ package bundle -type ParametersDefinition struct { - Fields map[string]ParameterDefinition `json:"fields" mapstructure:"fields"` - Required []string `json:"required,omitempty" mapstructure:"required,omitempty"` -} - -// ParameterDefinition defines a single parameter for a CNAB bundle -type ParameterDefinition struct { - Definition string `json:"definition" mapstructure:"definition"` - ApplyTo []string `json:"applyTo,omitempty" mapstructure:"applyTo,omitempty"` - Description string `json:"description,omitempty" mapstructure:"description"` - Destination *Location `json:"destination,omitemtpty" mapstructure:"destination"` - Immutable bool `json:"immutable,omitempty" mapstructure:"immutable,omitempty"` +// Parameter defines a single parameter for a CNAB bundle +type Parameter struct { + Definition string `json:"definition" yaml:"definition"` + ApplyTo []string `json:"applyTo,omitempty" yaml:"applyTo,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Destination *Location `json:"destination,omitemtpty" yaml:"destination,omitempty"` + Required bool `json:"required,omitempty" yaml:"required,omitempty"` } diff --git a/vendor/github.com/docker/cnab-to-oci/converter/convert.go b/vendor/github.com/docker/cnab-to-oci/converter/convert.go index 30e45c4..35a5f83 100644 --- a/vendor/github.com/docker/cnab-to-oci/converter/convert.go +++ b/vendor/github.com/docker/cnab-to-oci/converter/convert.go @@ -9,6 +9,7 @@ import ( "github.com/containerd/containerd/images" "github.com/deislabs/cnab-go/bundle" + "github.com/docker/cnab-to-oci/relocation" "github.com/docker/distribution/reference" ocischema "github.com/opencontainers/image-spec/specs-go" ocischemav1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -61,7 +62,7 @@ func GetBundleConfigManifestDescriptor(ix *ocischemav1.Index) (ocischemav1.Descr // ConvertBundleToOCIIndex converts a CNAB bundle into an OCI Index representation func ConvertBundleToOCIIndex(b *bundle.Bundle, targetRef reference.Named, - bundleConfigManifestRef ocischemav1.Descriptor, relocationMap bundle.ImageRelocationMap) (*ocischemav1.Index, error) { + bundleConfigManifestRef ocischemav1.Descriptor, relocationMap relocation.ImageRelocationMap) (*ocischemav1.Index, error) { annotations, err := makeAnnotations(b) if err != nil { return nil, err @@ -80,9 +81,9 @@ func ConvertBundleToOCIIndex(b *bundle.Bundle, targetRef reference.Named, return &result, nil } -// GenerateRelocationMap TODO -func GenerateRelocationMap(ix *ocischemav1.Index, b *bundle.Bundle, originRepo reference.Named) (bundle.ImageRelocationMap, error) { - relocationMap := bundle.ImageRelocationMap{} +// GenerateRelocationMap generates the bundle relocation map +func GenerateRelocationMap(ix *ocischemav1.Index, b *bundle.Bundle, originRepo reference.Named) (relocation.ImageRelocationMap, error) { + relocationMap := relocation.ImageRelocationMap{} for _, d := range ix.Manifests { switch d.MediaType { @@ -135,11 +136,6 @@ func GenerateRelocationMap(ix *ocischemav1.Index, b *bundle.Bundle, originRepo r return relocationMap, nil } -func CheckAnnotations(ix *ocischemav1.Index, b *bundle.Bundle) bool { - // TODO - return true -} - func makeAnnotations(b *bundle.Bundle) (map[string]string, error) { result := map[string]string{ CNABRuntimeVersionAnnotation: b.SchemaVersion, @@ -165,30 +161,8 @@ func makeAnnotations(b *bundle.Bundle) (map[string]string, error) { return result, nil } -func parseTopLevelAnnotations(annotations map[string]string, into *bundle.Bundle) error { - var ok bool - if into.Name, ok = annotations[ocischemav1.AnnotationTitle]; !ok { - return errors.New("manifest is missing title annotation " + ocischemav1.AnnotationTitle) - } - if into.Version, ok = annotations[ocischemav1.AnnotationVersion]; !ok { - return errors.New("manifest is missing version annotation " + ocischemav1.AnnotationVersion) - } - into.Description = annotations[ocischemav1.AnnotationDescription] - if maintainersJSON, ok := annotations[ocischemav1.AnnotationAuthors]; ok { - if err := json.Unmarshal([]byte(maintainersJSON), &into.Maintainers); err != nil { - return fmt.Errorf("unable to parse maintainers: %s", err) - } - } - if keywordsJSON, ok := annotations[CNABKeywordsAnnotation]; ok { - if err := json.Unmarshal([]byte(keywordsJSON), &into.Keywords); err != nil { - return fmt.Errorf("unable to parse keywords: %s", err) - } - } - return nil -} - func makeManifests(b *bundle.Bundle, targetReference reference.Named, - bundleConfigManifestReference ocischemav1.Descriptor, relocationMap bundle.ImageRelocationMap) ([]ocischemav1.Descriptor, error) { + bundleConfigManifestReference ocischemav1.Descriptor, relocationMap relocation.ImageRelocationMap) ([]ocischemav1.Descriptor, error) { if len(b.InvocationImages) != 1 { return nil, errors.New("only one invocation image supported") } @@ -230,10 +204,10 @@ func makeSortedImages(images map[string]bundle.Image) []string { return result } -func makeDescriptor(baseImage bundle.BaseImage, targetReference reference.Named, relocationMap bundle.ImageRelocationMap) (ocischemav1.Descriptor, error) { +func makeDescriptor(baseImage bundle.BaseImage, targetReference reference.Named, relocationMap relocation.ImageRelocationMap) (ocischemav1.Descriptor, error) { relocatedImage, ok := relocationMap[baseImage.Image] if !ok { - return ocischemav1.Descriptor{}, fmt.Errorf("unknown invocation image %q in the relocation map", baseImage.Image) + return ocischemav1.Descriptor{}, fmt.Errorf("image %q not present in the relocation map", baseImage.Image) } named, err := reference.ParseNormalizedNamed(relocatedImage) @@ -251,11 +225,14 @@ func makeDescriptor(baseImage bundle.BaseImage, targetReference reference.Named, if err != nil { return ocischemav1.Descriptor{}, err } + if baseImage.Size == 0 { + return ocischemav1.Descriptor{}, fmt.Errorf("image %q size is not set", relocatedImage) + } return ocischemav1.Descriptor{ Digest: digested.Digest(), MediaType: mediaType, - Size: int64(baseImage.Size), // TODO: mutate the bundle if the size is not set + Size: int64(baseImage.Size), }, nil } diff --git a/vendor/github.com/docker/cnab-to-oci/converter/types.go b/vendor/github.com/docker/cnab-to-oci/converter/types.go index 1b6dc93..1770221 100644 --- a/vendor/github.com/docker/cnab-to-oci/converter/types.go +++ b/vendor/github.com/docker/cnab-to-oci/converter/types.go @@ -82,14 +82,24 @@ func prepareOCIBundleConfig(mediaType string) bundleConfigPreparer { } } +func nonOCIDescriptorOf(blob []byte) distribution.Descriptor { + return distribution.Descriptor{ + MediaType: schema2.MediaTypeImageConfig, + Size: int64(len(blob)), + Digest: digest.FromBytes(blob), + } +} + func prepareNonOCIBundleConfig(blob []byte) (*PreparedBundleConfig, error) { + desc := nonOCIDescriptorOf(blob) man, err := schema2.FromStruct(schema2.Manifest{ Versioned: schema2.SchemaVersion, - Config: distribution.Descriptor{ - MediaType: schema2.MediaTypeImageConfig, - Size: int64(len(blob)), - Digest: digest.FromBytes(blob), + // Add a descriptor for the configuration because some registries + // require the layers property to be defined and non-empty + Layers: []distribution.Descriptor{ + desc, }, + Config: desc, }) if err != nil { return nil, err diff --git a/vendor/github.com/docker/cnab-to-oci/relocation/types.go b/vendor/github.com/docker/cnab-to-oci/relocation/types.go new file mode 100644 index 0000000..ffe4ea7 --- /dev/null +++ b/vendor/github.com/docker/cnab-to-oci/relocation/types.go @@ -0,0 +1,4 @@ +package relocation + +// ImageRelocationMap stores the mapping between the original image reference as key, and the relocated reference as a value. +type ImageRelocationMap map[string]string diff --git a/vendor/github.com/docker/cnab-to-oci/remotes/fixup.go b/vendor/github.com/docker/cnab-to-oci/remotes/fixup.go index 69d6fd2..4403940 100644 --- a/vendor/github.com/docker/cnab-to-oci/remotes/fixup.go +++ b/vendor/github.com/docker/cnab-to-oci/remotes/fixup.go @@ -11,17 +11,18 @@ import ( "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/remotes" "github.com/deislabs/cnab-go/bundle" + "github.com/docker/cnab-to-oci/relocation" "github.com/docker/distribution/reference" ocischemav1 "github.com/opencontainers/image-spec/specs-go/v1" ) // FixupBundle checks that all the references are present in the referenced repository, otherwise it will mount all // the manifests to that repository. The bundle is then patched with the new digested references. -func FixupBundle(ctx context.Context, b *bundle.Bundle, ref reference.Named, resolver remotes.Resolver, opts ...FixupOption) (bundle.ImageRelocationMap, error) { +func FixupBundle(ctx context.Context, b *bundle.Bundle, ref reference.Named, resolver remotes.Resolver, opts ...FixupOption) (relocation.ImageRelocationMap, error) { logger := log.G(ctx) logger.Debugf("Fixing up bundle %s", ref) - // Configure the fixup and the even loop + // Configure the fixup and the event loop cfg, err := newFixupConfig(b, ref, resolver, opts...) if err != nil { return nil, err @@ -46,22 +47,23 @@ func FixupBundle(ctx context.Context, b *bundle.Bundle, ref reference.Named, res return nil, fmt.Errorf("only one invocation image supported for bundle %q", ref) } - relocationMap := bundle.ImageRelocationMap{} - if err := fixupImage(ctx, b.InvocationImages[0].BaseImage, relocationMap, cfg, events, cfg.invocationImagePlatformFilter); err != nil { + relocationMap := relocation.ImageRelocationMap{} + if err := fixupImage(ctx, &b.InvocationImages[0].BaseImage, relocationMap, cfg, events, cfg.invocationImagePlatformFilter); err != nil { return nil, err } // Fixup images - for _, original := range b.Images { - if err := fixupImage(ctx, original.BaseImage, relocationMap, cfg, events, cfg.componentImagePlatformFilter); err != nil { + for name, original := range b.Images { + if err := fixupImage(ctx, &original.BaseImage, relocationMap, cfg, events, cfg.componentImagePlatformFilter); err != nil { return nil, err } + b.Images[name] = original } logger.Debug("Bundle fixed") return relocationMap, nil } -func fixupImage(ctx context.Context, baseImage bundle.BaseImage, relocationMap bundle.ImageRelocationMap, cfg fixupConfig, events chan<- FixupEvent, platformFilter platforms.Matcher) error { +func fixupImage(ctx context.Context, baseImage *bundle.BaseImage, relocationMap relocation.ImageRelocationMap, cfg fixupConfig, events chan<- FixupEvent, platformFilter platforms.Matcher) error { log.G(ctx).Debugf("Updating entry in relocation map for %q", baseImage.Image) ctx = withMutedContext(ctx) notifyEvent, progress := makeEventNotifier(events, baseImage.Image, cfg.targetRef) @@ -77,10 +79,26 @@ func fixupImage(ctx context.Context, baseImage bundle.BaseImage, relocationMap b if err != nil { return err } + relocationMap[baseImage.Image] = newRef.String() - fmt.Printf("adding entry in relocation map: %v: %v", baseImage.Image, newRef.String()) - // TODO: check if the relocation map should be updated here as well + // if the autoUpdateBundle flag is passed, mutate the bundle with the resolved digest, mediaType, and size + if cfg.autoBundleUpdate { + baseImage.Digest = fixupInfo.resolvedDescriptor.Digest.String() + baseImage.Size = uint64(fixupInfo.resolvedDescriptor.Size) + baseImage.MediaType = fixupInfo.resolvedDescriptor.MediaType + } else { + if baseImage.Digest != fixupInfo.resolvedDescriptor.Digest.String() { + return fmt.Errorf("image %q digest differs %q after fixup: %q", baseImage.Image, baseImage.Digest, fixupInfo.resolvedDescriptor.Digest.String()) + } + if baseImage.Size != uint64(fixupInfo.resolvedDescriptor.Size) { + return fmt.Errorf("image %q size differs %d after fixup: %d", baseImage.Image, baseImage.Size, fixupInfo.resolvedDescriptor.Size) + } + if baseImage.MediaType != fixupInfo.resolvedDescriptor.MediaType { + return fmt.Errorf("image %q media type differs %q after fixup: %q", baseImage.Image, baseImage.MediaType, fixupInfo.resolvedDescriptor.MediaType) + } + } + if fixupInfo.sourceRef.Name() == fixupInfo.targetRepo.Name() { notifyEvent(FixupEventTypeCopyImageEnd, "Nothing to do: image reference is already present in repository"+fixupInfo.targetRepo.String(), nil) return nil @@ -110,9 +128,18 @@ func fixupImage(ctx context.Context, baseImage bundle.BaseImage, relocationMap b return nil } -func fixupPlatforms(ctx context.Context, baseImage bundle.BaseImage, relocationMap bundle.ImageRelocationMap, fixupInfo *imageFixupInfo, sourceFetcher sourceFetcherAdder, filter platforms.Matcher) error { +func fixupPlatforms(ctx context.Context, + baseImage *bundle.BaseImage, + relocationMap relocation.ImageRelocationMap, + fixupInfo *imageFixupInfo, + sourceFetcher sourceFetcherAdder, + filter platforms.Matcher) error { + + logger := log.G(ctx) + logger.Debugf("Fixup platforms for image %v, with relocation map %v", baseImage, relocationMap) if filter == nil || - (fixupInfo.resolvedDescriptor.MediaType != ocischemav1.MediaTypeImageIndex && fixupInfo.resolvedDescriptor.MediaType != images.MediaTypeDockerSchema2ManifestList) { + (fixupInfo.resolvedDescriptor.MediaType != ocischemav1.MediaTypeImageIndex && + fixupInfo.resolvedDescriptor.MediaType != images.MediaTypeDockerSchema2ManifestList) { // no platform filter if platform is empty, or if the descriptor is not an OCI Index / Docker Manifest list return nil } @@ -155,7 +182,7 @@ func fixupPlatforms(ctx context.Context, baseImage bundle.BaseImage, relocationM } func fixupBaseImage(ctx context.Context, - baseImage bundle.BaseImage, + baseImage *bundle.BaseImage, targetRef reference.Named, //nolint: interfacer resolver remotes.Resolver) (imageFixupInfo, error) { diff --git a/vendor/github.com/docker/cnab-to-oci/remotes/fixuphelpers.go b/vendor/github.com/docker/cnab-to-oci/remotes/fixuphelpers.go index 04435e5..0255b31 100644 --- a/vendor/github.com/docker/cnab-to-oci/remotes/fixuphelpers.go +++ b/vendor/github.com/docker/cnab-to-oci/remotes/fixuphelpers.go @@ -102,7 +102,7 @@ func notifyError(notifyEvent eventNotifier, err error) error { return err } -func checkBaseImage(baseImage bundle.BaseImage) error { +func checkBaseImage(baseImage *bundle.BaseImage) error { switch baseImage.ImageType { case "docker": case "oci": @@ -119,7 +119,7 @@ func checkBaseImage(baseImage bundle.BaseImage) error { case images.MediaTypeDockerSchema2ManifestList: case "": default: - return fmt.Errorf("image media type %q is not supported", baseImage.ImageType) + return fmt.Errorf("image media type %q is not supported", baseImage.MediaType) } return nil diff --git a/vendor/github.com/docker/cnab-to-oci/remotes/fixupoptions.go b/vendor/github.com/docker/cnab-to-oci/remotes/fixupoptions.go index 698fd3d..118bbb3 100644 --- a/vendor/github.com/docker/cnab-to-oci/remotes/fixupoptions.go +++ b/vendor/github.com/docker/cnab-to-oci/remotes/fixupoptions.go @@ -25,6 +25,7 @@ type fixupConfig struct { resolver remotes.Resolver invocationImagePlatformFilter platforms.Matcher componentImagePlatformFilter platforms.Matcher + autoBundleUpdate bool } // FixupOption is a helper for configuring a FixupBundle @@ -105,3 +106,11 @@ func WithParallelism(maxConcurrentJobs int, jobsBufferLength int) FixupOption { return nil } } + +// WithAutoBundleUpdate updates the bundle with content digests and size provided by the registry +func WithAutoBundleUpdate() FixupOption { + return func(cfg *fixupConfig) error { + cfg.autoBundleUpdate = true + return nil + } +} diff --git a/vendor/github.com/docker/cnab-to-oci/remotes/pull.go b/vendor/github.com/docker/cnab-to-oci/remotes/pull.go index 3fbb03f..a212b84 100644 --- a/vendor/github.com/docker/cnab-to-oci/remotes/pull.go +++ b/vendor/github.com/docker/cnab-to-oci/remotes/pull.go @@ -12,13 +12,14 @@ import ( "github.com/deislabs/cnab-go/bundle" "github.com/docker/cli/opts" "github.com/docker/cnab-to-oci/converter" + "github.com/docker/cnab-to-oci/relocation" "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/client/auth" ocischemav1 "github.com/opencontainers/image-spec/specs-go/v1" ) // Pull pulls a bundle from an OCI Image Index manifest -func Pull(ctx context.Context, ref reference.Named, resolver remotes.Resolver) (*bundle.Bundle, bundle.ImageRelocationMap, error) { +func Pull(ctx context.Context, ref reference.Named, resolver remotes.Resolver) (*bundle.Bundle, relocation.ImageRelocationMap, error) { log.G(ctx).Debugf("Pulling CNAB Bundle %s", ref) index, err := getIndex(ctx, ref, resolver) if err != nil { diff --git a/vendor/github.com/docker/cnab-to-oci/remotes/push.go b/vendor/github.com/docker/cnab-to-oci/remotes/push.go index 8594397..d72c434 100644 --- a/vendor/github.com/docker/cnab-to-oci/remotes/push.go +++ b/vendor/github.com/docker/cnab-to-oci/remotes/push.go @@ -11,6 +11,7 @@ import ( "github.com/containerd/containerd/remotes" "github.com/deislabs/cnab-go/bundle" "github.com/docker/cnab-to-oci/converter" + "github.com/docker/cnab-to-oci/relocation" "github.com/docker/distribution/reference" "github.com/opencontainers/go-digest" ocischemav1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -21,7 +22,13 @@ import ( type ManifestOption func(*ocischemav1.Index) error // Push pushes a bundle as an OCI Image Index manifest -func Push(ctx context.Context, b *bundle.Bundle, relocationMap bundle.ImageRelocationMap, ref reference.Named, resolver remotes.Resolver, allowFallbacks bool, options ...ManifestOption) (ocischemav1.Descriptor, error) { +func Push(ctx context.Context, + b *bundle.Bundle, + relocationMap relocation.ImageRelocationMap, + ref reference.Named, + resolver remotes.Resolver, + allowFallbacks bool, + options ...ManifestOption) (ocischemav1.Descriptor, error) { log.G(ctx).Debugf("Pushing CNAB Bundle %s", ref) confManifestDescriptor, err := pushConfig(ctx, b, ref, resolver, allowFallbacks) @@ -59,7 +66,7 @@ func pushConfig(ctx context.Context, return confManifestDescriptor, nil } -func pushIndex(ctx context.Context, b *bundle.Bundle, relocationMap bundle.ImageRelocationMap, ref reference.Named, resolver remotes.Resolver, allowFallbacks bool, +func pushIndex(ctx context.Context, b *bundle.Bundle, relocationMap relocation.ImageRelocationMap, ref reference.Named, resolver remotes.Resolver, allowFallbacks bool, confManifestDescriptor ocischemav1.Descriptor, options ...ManifestOption) (ocischemav1.Descriptor, error) { logger := log.G(ctx) logger.Debug("Pushing CNAB Index") @@ -76,8 +83,10 @@ func pushIndex(ctx context.Context, b *bundle.Bundle, relocationMap bundle.Image if err := pushPayload(ctx, resolver, ref.String(), indexDescriptor, indexPayload); err != nil { if !allowFallbacks { + logger.Debug("Not using fallbacks, giving up") return ocischemav1.Descriptor{}, err } + logger.Debugf("Unable to push OCI Index: %v", err) // retry with a docker manifestlist return pushDockerManifestList(ctx, b, relocationMap, ref, resolver, confManifestDescriptor, options...) } @@ -86,7 +95,7 @@ func pushIndex(ctx context.Context, b *bundle.Bundle, relocationMap bundle.Image return indexDescriptor, nil } -func pushDockerManifestList(ctx context.Context, b *bundle.Bundle, relocationMap bundle.ImageRelocationMap, ref reference.Named, resolver remotes.Resolver, +func pushDockerManifestList(ctx context.Context, b *bundle.Bundle, relocationMap relocation.ImageRelocationMap, ref reference.Named, resolver remotes.Resolver, confManifestDescriptor ocischemav1.Descriptor, options ...ManifestOption) (ocischemav1.Descriptor, error) { logger := log.G(ctx) @@ -99,13 +108,20 @@ func pushDockerManifestList(ctx context.Context, b *bundle.Bundle, relocationMap logger.Debug("Manifest list Descriptor") logPayload(logger, indexDescriptor) - if err := pushPayload(ctx, resolver, ref.String(), indexDescriptor, indexPayload); err != nil { + if err := pushPayload(ctx, + resolver, ref.String(), + indexDescriptor, + indexPayload); err != nil { return ocischemav1.Descriptor{}, err } return indexDescriptor, nil } -func prepareIndex(b *bundle.Bundle, relocationMap bundle.ImageRelocationMap, ref reference.Named, confDescriptor ocischemav1.Descriptor, options ...ManifestOption) (ocischemav1.Descriptor, []byte, error) { +func prepareIndex(b *bundle.Bundle, + relocationMap relocation.ImageRelocationMap, + ref reference.Named, + confDescriptor ocischemav1.Descriptor, + options ...ManifestOption) (ocischemav1.Descriptor, []byte, error) { ix, err := convertIndexAndApplyOptions(b, relocationMap, ref, confDescriptor, options...) if err != nil { return ocischemav1.Descriptor{}, nil, err @@ -127,7 +143,11 @@ type ociIndexWrapper struct { MediaType string `json:"mediaType,omitempty"` } -func convertIndexAndApplyOptions(b *bundle.Bundle, relocationMap bundle.ImageRelocationMap, ref reference.Named, confDescriptor ocischemav1.Descriptor, options ...ManifestOption) (*ocischemav1.Index, error) { +func convertIndexAndApplyOptions(b *bundle.Bundle, + relocationMap relocation.ImageRelocationMap, + ref reference.Named, + confDescriptor ocischemav1.Descriptor, + options ...ManifestOption) (*ocischemav1.Index, error) { ix, err := converter.ConvertBundleToOCIIndex(b, ref, confDescriptor, relocationMap) if err != nil { return nil, err @@ -140,7 +160,11 @@ func convertIndexAndApplyOptions(b *bundle.Bundle, relocationMap bundle.ImageRel return ix, nil } -func prepareIndexNonOCI(b *bundle.Bundle, relocationMap bundle.ImageRelocationMap, ref reference.Named, confDescriptor ocischemav1.Descriptor, options ...ManifestOption) (ocischemav1.Descriptor, []byte, error) { +func prepareIndexNonOCI(b *bundle.Bundle, + relocationMap relocation.ImageRelocationMap, + ref reference.Named, + confDescriptor ocischemav1.Descriptor, + options ...ManifestOption) (ocischemav1.Descriptor, []byte, error) { ix, err := convertIndexAndApplyOptions(b, relocationMap, ref, confDescriptor, options...) if err != nil { return ocischemav1.Descriptor{}, nil, err