diff --git a/cmd/tuf-notary/init.go b/cmd/tuf-notary/init.go index 92a09e4..d40ee42 100644 --- a/cmd/tuf-notary/init.go +++ b/cmd/tuf-notary/init.go @@ -40,7 +40,7 @@ func cmdInit(args []string, opts docopt.Opts) error { if err != nil { return fmt.Errorf("failed to read %s: %w", filename, err) } - root_desc, err := tufnotary.UploadTUFMetadata(registry, repository, "root", contents, "") + root_desc, err := tufnotary.UploadTUFMetadata(registry, repository, "root", contents) if err != nil { return err } @@ -52,7 +52,7 @@ func cmdInit(args []string, opts docopt.Opts) error { if err != nil { return fmt.Errorf("failed to read %s: %w", filename, err) } - targets_desc, err := tufnotary.UploadTUFMetadata(registry, repository, "targets", contents, "root") + targets_desc, err := tufnotary.UploadTUFMetadataWithReference(registry, repository, "targets", contents, root_desc) if err != nil { return err } diff --git a/go.mod b/go.mod index 14d0f54..2c67fcf 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( github.com/moby/term v0.0.0-20200312100748-672ec06f55cd // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/oras-project/artifacts-spec v1.0.0-draft.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.7.1 // indirect diff --git a/go.sum b/go.sum index b852afe..5d7f318 100644 --- a/go.sum +++ b/go.sum @@ -583,6 +583,8 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/oras-project/artifacts-spec v1.0.0-draft.1.1 h1:2YMUDyDH0glYA4gNG/zEg9HNVzgGX8kr/NBLR9AQkLQ= +github.com/oras-project/artifacts-spec v1.0.0-draft.1.1/go.mod h1:Xch2aLzSwtkhbFFN6LUzTfLtukYvMMdXJ4oZ8O7BOdc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= diff --git a/registry-access.go b/registry-access.go index 82bee66..0e264fa 100644 --- a/registry-access.go +++ b/registry-access.go @@ -2,13 +2,16 @@ package tufnotary import ( "context" + "encoding/json" + "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" + artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1" "oras.land/oras-go/pkg/content" "oras.land/oras-go/pkg/oras" ) -func UploadTUFMetadata(registry string, repository string, name string, contents []byte, reference string) (ocispec.Descriptor, error) { +func UploadTUFMetadataWithReference(registry string, repository string, name string, contents []byte, reference ocispec.Descriptor) (ocispec.Descriptor, error) { ref := registry + "/" + repository + ":" + name fileName := repository + "/staged/" + name + ".json" @@ -16,7 +19,77 @@ func UploadTUFMetadata(registry string, repository string, name string, contents ctx := context.Background() - // TODO: add reference once it's supported in oras-go: https://github.com/oras-project/oras-go/pull/35 + memoryStore := content.NewMemory() + desc, err := memoryStore.Add(fileName, mediaType, contents) + if err != nil { + return ocispec.Descriptor{}, err + } + + config, configDesc, err := content.GenerateConfig(nil) + if err != nil { + return ocispec.Descriptor{}, err + } + + var descs []artifactspec.Descriptor + descs = append(descs, artifactspec.Descriptor{ + MediaType: desc.MediaType, + Digest: desc.Digest, + Size: desc.Size, + URLs: desc.URLs, + Annotations: desc.Annotations, + }) + + manifest := artifactspec.Manifest{ + MediaType: "application/vnd.cncf.oras.artifact.manifest.v1+json", + ArtifactType: mediaType, + Blobs: descs, + Annotations: nil, + Subject: artifactspec.Descriptor{ + MediaType: reference.MediaType, + Digest: reference.Digest, + Size: reference.Size, + URLs: reference.URLs, + Annotations: reference.Annotations, + }, + } + + manifestBytes, err := json.Marshal(manifest) + if err != nil { + return ocispec.Descriptor{}, err + } + + manifestDescriptor := ocispec.Descriptor{ + MediaType: ocispec.MediaTypeImageManifest, + Digest: digest.FromBytes(manifestBytes), + Size: int64(len(manifestBytes)), + } + + memoryStore.Set(configDesc, config) + err = memoryStore.StoreManifest(ref, manifestDescriptor, manifestBytes) + if err != nil { + return ocispec.Descriptor{}, err + } + + reg, err := content.NewRegistry(content.RegistryOptions{PlainHTTP: true}) + if err != nil { + return ocispec.Descriptor{}, err + } + + desc, err = oras.Copy(ctx, memoryStore, ref, reg, "") + if err != nil { + return ocispec.Descriptor{}, err + } + + return desc, nil +} + +func UploadTUFMetadata(registry string, repository string, name string, contents []byte) (ocispec.Descriptor, error) { + ref := registry + "/" + repository + ":" + name + fileName := repository + "/staged/" + name + ".json" + + mediaType := "application/vnd.cncf.notary.tuf+json" + + ctx := context.Background() memoryStore := content.NewMemory() desc, err := memoryStore.Add(fileName, mediaType, contents) diff --git a/registry-access_test.go b/registry-access_test.go index cf05655..07529ba 100644 --- a/registry-access_test.go +++ b/registry-access_test.go @@ -43,16 +43,29 @@ func (suite *RegistryTestSuite) TestUploadTUFMetadata() { assert.Nil(suite.T(), err) //good case - desc, err := UploadTUFMetadata(suite.RegistryHost, "test-tuf-repo", "root", contents, "") + desc, err := UploadTUFMetadata(suite.RegistryHost, "test-tuf-repo", "root", contents) assert.Nil(suite.T(), err) assert.True(suite.T(), strings.HasPrefix(desc.Digest.String(), "sha256")) //bad registry badHost := fmt.Sprintf("localhost:%d", 2) - desc, err = UploadTUFMetadata(badHost, "test-tuf-repo", "root", contents, "") + desc, err = UploadTUFMetadata(badHost, "test-tuf-repo", "root", contents) assert.NotNil(suite.T(), err) } +func (suite *RegistryTestSuite) TestUploadTUFMetadataWithReference() { + contents, err := ioutil.ReadFile("test/tuf-repo/staged/root.json") + assert.Nil(suite.T(), err) + + //good case + // TODO update test registry to allow references + _, err = UploadTUFMetadata(suite.RegistryHost, "test-tuf-repo", "root", contents) + //assert.Nil(suite.T(), err) + //targetDesc, err := UploadTUFMetadataWithReference(suite.RegistryHost, "test-tuf-repo", "targets", contents, desc) + //assert.Nil(suite.T(), err) + //assert.True(suite.T(), strings.HasPrefix(targetDesc.Digest.String(), "sha256")) +} + func TestRegistryTestSuite(t *testing.T) { suite.Run(t, new(RegistryTestSuite)) }