diff --git a/oci/client/client.go b/oci/client/client.go index b5c5d6cc..b3cd257a 100644 --- a/oci/client/client.go +++ b/oci/client/client.go @@ -20,7 +20,6 @@ import ( "context" "github.com/google/go-containerregistry/pkg/crane" - gcrv1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/fluxcd/pkg/oci" @@ -41,16 +40,9 @@ func NewClient(opts []crane.Option) *Client { return &Client{options: options} } -// DefaultOptions returns an array containing crane.WithPlatform -// to set the platform to flux. +// DefaultOptions returns an empty list of client options. func DefaultOptions() []crane.Option { - return []crane.Option{ - crane.WithPlatform(&gcrv1.Platform{ - Architecture: "flux", - OS: "flux", - OSVersion: "v2", - }), - } + return []crane.Option{} } // GetOptions returns the list of crane.Option used by this Client. diff --git a/oci/client/list.go b/oci/client/list.go index 8c083c13..32fb82f5 100644 --- a/oci/client/list.go +++ b/oci/client/list.go @@ -104,11 +104,10 @@ func (c *Client) List(ctx context.Context, url string, opts ListOptions) ([]Meta return nil, fmt.Errorf("parsing manifest failed: %w", err) } - if m, err := MetadataFromAnnotations(manifest.Annotations); err == nil { - meta.Revision = m.Revision - meta.Source = m.Source - meta.Created = m.Created - } + manifestMetadata := MetadataFromAnnotations(manifest.Annotations) + meta.Revision = manifestMetadata.Revision + meta.Source = manifestMetadata.Source + meta.Created = manifestMetadata.Created digest, err := crane.Digest(meta.URL, c.optionsWithContext(ctx)...) if err != nil { diff --git a/oci/client/meta.go b/oci/client/meta.go index 9a4d287a..592f8234 100644 --- a/oci/client/meta.go +++ b/oci/client/meta.go @@ -17,17 +17,15 @@ limitations under the License. package client import ( - "fmt" - "github.com/fluxcd/pkg/oci" ) // Metadata holds the upstream information about on artifact's source. // https://github.com/opencontainers/image-spec/blob/main/annotations.md type Metadata struct { - Created string `json:"created"` - Source string `json:"source_url"` - Revision string `json:"source_revision"` + Created string `json:"created,omitempty"` + Source string `json:"source_url,omitempty"` + Revision string `json:"source_revision,omitempty"` Digest string `json:"digest"` URL string `json:"url"` Annotations map[string]string `json:"annotations,omitempty"` @@ -49,28 +47,11 @@ func (m *Metadata) ToAnnotations() map[string]string { } // MetadataFromAnnotations parses the OpenContainers annotations and returns a Metadata object. -func MetadataFromAnnotations(annotations map[string]string) (*Metadata, error) { - created, ok := annotations[oci.CreatedAnnotation] - if !ok { - return nil, fmt.Errorf("'%s' annotation not found", oci.CreatedAnnotation) - } - - source, ok := annotations[oci.SourceAnnotation] - if !ok { - return nil, fmt.Errorf("'%s' annotation not found", oci.SourceAnnotation) - } - - revision, ok := annotations[oci.RevisionAnnotation] - if !ok { - return nil, fmt.Errorf("'%s' annotation not found", oci.RevisionAnnotation) - } - - m := Metadata{ - Created: created, - Source: source, - Revision: revision, +func MetadataFromAnnotations(annotations map[string]string) *Metadata { + return &Metadata{ + Created: annotations[oci.CreatedAnnotation], + Source: annotations[oci.SourceAnnotation], + Revision: annotations[oci.RevisionAnnotation], Annotations: annotations, } - - return &m, nil } diff --git a/oci/client/pull.go b/oci/client/pull.go index cbc5e6de..d4828687 100644 --- a/oci/client/pull.go +++ b/oci/client/pull.go @@ -47,10 +47,8 @@ func (c *Client) Pull(ctx context.Context, url, outDir string) (*Metadata, error return nil, fmt.Errorf("parsing manifest failed: %w", err) } - meta, err := MetadataFromAnnotations(manifest.Annotations) - if err != nil { - return nil, err - } + meta := MetadataFromAnnotations(manifest.Annotations) + meta.URL = url meta.Digest = ref.Context().Digest(digest.String()).String() layers, err := img.Layers() diff --git a/oci/client/pull_test.go b/oci/client/pull_test.go new file mode 100644 index 00000000..772ffd20 --- /dev/null +++ b/oci/client/pull_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2023 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import ( + "context" + "fmt" + "path/filepath" + "testing" + + "github.com/fluxcd/pkg/oci" + "github.com/google/go-containerregistry/pkg/crane" + "github.com/google/go-containerregistry/pkg/v1/empty" + "github.com/google/go-containerregistry/pkg/v1/mutate" + "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/google/go-containerregistry/pkg/v1/types" + . "github.com/onsi/gomega" +) + +func Test_PullAnyTarball(t *testing.T) { + g := NewWithT(t) + ctx := context.Background() + c := NewClient(DefaultOptions()) + testDir := "testdata/artifact" + + tag := "latest" + repo := "test-no-annotations" + randStringRunes(5) + + dst := fmt.Sprintf("%s/%s:%s", dockerReg, repo, tag) + + artifact := filepath.Join(t.TempDir(), "artifact.tgz") + g.Expect(c.Build(artifact, testDir, nil)).To(Succeed()) + + img := mutate.MediaType(empty.Image, types.OCIManifestSchema1) + img = mutate.ConfigMediaType(img, oci.CanonicalConfigMediaType) + + layer, err := tarball.LayerFromFile(artifact, tarball.WithMediaType("application/vnd.acme.some.content.layer.v1.tar+gzip")) + g.Expect(err).ToNot(HaveOccurred()) + + img, err = mutate.Append(img, mutate.Addendum{Layer: layer}) + g.Expect(err).ToNot(HaveOccurred()) + + g.Expect(crane.Push(img, dst, c.optionsWithContext(ctx)...)).ToNot(HaveOccurred()) + + extractTo := filepath.Join(t.TempDir(), "artifact") + m, err := c.Pull(ctx, dst, extractTo) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(m).ToNot(BeNil()) + g.Expect(m.Annotations).To(BeEmpty()) + g.Expect(m.Created).To(BeEmpty()) + g.Expect(m.Revision).To(BeEmpty()) + g.Expect(m.Source).To(BeEmpty()) + g.Expect(m.URL).To(Equal(dst)) + g.Expect(m.Digest).ToNot(BeEmpty()) + g.Expect(extractTo).To(BeADirectory()) + + for _, entry := range []string{ + "deploy", + "deploy/repo.yaml", + "deployment.yaml", + "ignore-dir", + "ignore-dir/deployment.yaml", + "ignore.txt", + "somedir", + "somedir/repo.yaml", + "somedir/git/repo.yaml", + } { + g.Expect(extractTo + "/" + entry).To(Or(BeAnExistingFile(), BeADirectory())) + } +}