From e91dbea76d6fae6630b6f4775bba6fd5fb6a676b Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Sat, 11 Jan 2020 16:26:13 +0800 Subject: [PATCH] Implement the manifest v1 image resolver The manifest v1 image resolver populates the architecture info into the artifact Signed-off-by: Wenkai Yin --- .../abstractor/resolver/image/manifest_v1.go | 10 ++- .../resolver/image/manifest_v1_test.go | 87 +++++++++++++++++++ .../abstractor/resolver/image/manifest_v2.go | 11 +-- 3 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 src/api/artifact/abstractor/resolver/image/manifest_v1_test.go diff --git a/src/api/artifact/abstractor/resolver/image/manifest_v1.go b/src/api/artifact/abstractor/resolver/image/manifest_v1.go index 1e25c2efa..76cc4d594 100644 --- a/src/api/artifact/abstractor/resolver/image/manifest_v1.go +++ b/src/api/artifact/abstractor/resolver/image/manifest_v1.go @@ -12,6 +12,7 @@ package image import ( "context" + "encoding/json" "github.com/docker/distribution/manifest/schema1" "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver" "github.com/goharbor/harbor/src/common/utils/log" @@ -35,6 +36,13 @@ func (m *manifestV1Resolver) ArtifactType() string { } func (m *manifestV1Resolver) Resolve(ctx context.Context, manifest []byte, artifact *artifact.Artifact) error { - // TODO implement + mani := &schema1.Manifest{} + if err := json.Unmarshal([]byte(manifest), mani); err != nil { + return err + } + if artifact.ExtraAttrs == nil { + artifact.ExtraAttrs = map[string]interface{}{} + } + artifact.ExtraAttrs["architecture"] = mani.Architecture return nil } diff --git a/src/api/artifact/abstractor/resolver/image/manifest_v1_test.go b/src/api/artifact/abstractor/resolver/image/manifest_v1_test.go new file mode 100644 index 000000000..8fdcce10c --- /dev/null +++ b/src/api/artifact/abstractor/resolver/image/manifest_v1_test.go @@ -0,0 +1,87 @@ +// 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 image + +import ( + "github.com/goharbor/harbor/src/pkg/artifact" + "github.com/stretchr/testify/suite" + "testing" +) + +type manifestV1ResolverTestSuite struct { + suite.Suite + resolver *manifestV1Resolver +} + +func (m *manifestV1ResolverTestSuite) SetupSuite() { + m.resolver = &manifestV1Resolver{} + +} + +func (m *manifestV1ResolverTestSuite) TestArtifactType() { + m.Assert().Equal(ArtifactTypeImage, m.resolver.ArtifactType()) +} + +func (m *manifestV1ResolverTestSuite) TestResolve() { + manifest := `{ + "name": "hello-world", + "tag": "latest", + "architecture": "amd64", + "fsLayers": [ + { + "blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + }, + { + "blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + }, + { + "blobSum": "sha256:cc8567d70002e957612902a8e985ea129d831ebe04057d88fb644857caa45d11" + }, + { + "blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + } + ], + "history": [ + { + "v1Compatibility": "{\"id\":\"e45a5af57b00862e5ef5782a9925979a02ba2b12dff832fd0991335f4a11e5c5\",\"parent\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"created\":\"2014-12-31T22:57:59.178729048Z\",\"container\":\"27b45f8fb11795b52e9605b686159729b0d9ca92f76d40fb4f05a62e19c46b4f\",\"container_config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) CMD [/hello]\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"docker_version\":\"1.4.1\",\"config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/hello\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"architecture\":\"amd64\",\"os\":\"linux\",\"Size\":0}\n" + }, + { + "v1Compatibility": "{\"id\":\"e45a5af57b00862e5ef5782a9925979a02ba2b12dff832fd0991335f4a11e5c5\",\"parent\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"created\":\"2014-12-31T22:57:59.178729048Z\",\"container\":\"27b45f8fb11795b52e9605b686159729b0d9ca92f76d40fb4f05a62e19c46b4f\",\"container_config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) CMD [/hello]\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"docker_version\":\"1.4.1\",\"config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/hello\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"architecture\":\"amd64\",\"os\":\"linux\",\"Size\":0}\n" + } + ], + "schemaVersion": 1, + "signatures": [ + { + "header": { + "jwk": { + "crv": "P-256", + "kid": "OD6I:6DRK:JXEJ:KBM4:255X:NSAA:MUSF:E4VM:ZI6W:CUN2:L4Z6:LSF4", + "kty": "EC", + "x": "3gAwX48IQ5oaYQAYSxor6rYYc_6yjuLCjtQ9LUakg4A", + "y": "t72ge6kIA1XOjqjVoEOiPPAURltJFBMGDSQvEGVB010" + }, + "alg": "ES256" + }, + "signature": "XREm0L8WNn27Ga_iE_vRnTxVMhhYY0Zst_FfkKopg6gWSoTOZTuW4rK0fg_IqnKkEKlbD83tD46LKEGi5aIVFg", + "protected": "eyJmb3JtYXRMZW5ndGgiOjY2MjgsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAxNS0wNC0wOFQxODo1Mjo1OVoifQ" + } + ] +} +` + artifact := &artifact.Artifact{} + err := m.resolver.Resolve(nil, []byte(manifest), artifact) + m.Require().Nil(err) + m.Assert().Equal("amd64", artifact.ExtraAttrs["architecture"].(string)) +} + +func TestManifestV1ResolverTestSuite(t *testing.T) { + suite.Run(t, &manifestV1ResolverTestSuite{}) +} diff --git a/src/api/artifact/abstractor/resolver/image/manifest_v2.go b/src/api/artifact/abstractor/resolver/image/manifest_v2.go index 7238cb82d..6a55bd798 100644 --- a/src/api/artifact/abstractor/resolver/image/manifest_v2.go +++ b/src/api/artifact/abstractor/resolver/image/manifest_v2.go @@ -66,11 +66,12 @@ func (m *manifestV2Resolver) Resolve(ctx context.Context, content []byte, artifa if err := json.Unmarshal(layer, image); err != nil { return err } - artifact.ExtraAttrs = map[string]interface{}{ - "created": image.Created, - "author": image.Author, - "architecture": image.Architecture, - "os": image.OS, + if artifact.ExtraAttrs == nil { + artifact.ExtraAttrs = map[string]interface{}{} } + artifact.ExtraAttrs["created"] = image.Created + artifact.ExtraAttrs["author"] = image.Author + artifact.ExtraAttrs["architecture"] = image.Architecture + artifact.ExtraAttrs["os"] = image.OS return nil }