diff --git a/internal/image/docker_schema1_test.go b/internal/image/docker_schema1_test.go index 58f222b3c7..6a24c01c01 100644 --- a/internal/image/docker_schema1_test.go +++ b/internal/image/docker_schema1_test.go @@ -273,56 +273,71 @@ func TestManifestSchema1Inspect(t *testing.T) { manifestSchema1FromFixture(t, "schema1.json"), manifestSchema1FromComponentsLikeFixture(t), } { + + labels := map[string]string{ + "Kolla-SHA": "5.0.0-39-g6f1b947b", + "architecture": "x86_64", + "authoritative-source-url": "registry.access.redhat.com", + "build-date": "2018-01-25T00:32:27.807261", + "com.redhat.build-host": "ip-10-29-120-186.ec2.internal", + "com.redhat.component": "openstack-nova-api-docker", + "description": "Red Hat OpenStack Platform 12.0 nova-api", + "distribution-scope": "public", + "io.k8s.description": "Red Hat OpenStack Platform 12.0 nova-api", + "io.k8s.display-name": "Red Hat OpenStack Platform 12.0 nova-api", + "io.openshift.tags": "rhosp osp openstack osp-12.0", + "kolla_version": "stable/pike", + "name": "rhosp12/openstack-nova-api", + "release": "20180124.1", + "summary": "Red Hat OpenStack Platform 12.0 nova-api", + "tripleo-common_version": "7.6.3-23-g4891cfe", + "url": "https://access.redhat.com/containers/#/registry.access.redhat.com/rhosp12/openstack-nova-api/images/12.0-20180124.1", + "vcs-ref": "9b31243b7b448eb2fc3b6e2c96935b948f806e98", + "vcs-type": "git", + "vendor": "Red Hat, Inc.", + "version": "12.0", + "version-release": "12.0-20180124.1", + } + + Layers := []string{ + "sha256:9cadd93b16ff2a0c51ac967ea2abfadfac50cfa3af8b5bf983d89b8f8647f3e4", + "sha256:4aa565ad8b7a87248163ce7dba1dd3894821aac97e846b932ff6b8ef9a8a508a", + "sha256:f576d102e09b9eef0e305aaef705d2d43a11bebc3fd5810a761624bd5e11997e", + "sha256:9e92df2aea7dc0baf5f1f8d509678d6a6306de27ad06513f8e218371938c07a6", + "sha256:62e48e39dc5b30b75a97f05bccc66efbae6058b860ee20a5c9a184b9d5e25788", + "sha256:e623934bca8d1a74f51014256445937714481e49343a31bda2bc5f534748184d", + } + + Env := []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "container=oci", + "KOLLA_BASE_DISTRO=rhel", + "KOLLA_INSTALL_TYPE=binary", + "KOLLA_INSTALL_METATYPE=rhos", + "PS1=$(tput bold)($(printenv KOLLA_SERVICE_NAME))$(tput sgr0)[$(id -un)@$(hostname -s) $(pwd)]$ ", + } + ii, err := m.Inspect(context.Background()) require.NoError(t, err) created := time.Date(2018, 1, 25, 0, 37, 48, 268558000, time.UTC) - assert.Equal(t, types.ImageInspectInfo{ - Tag: "latest", - Created: &created, - DockerVersion: "1.12.6", - Labels: map[string]string{ - "Kolla-SHA": "5.0.0-39-g6f1b947b", - "architecture": "x86_64", - "authoritative-source-url": "registry.access.redhat.com", - "build-date": "2018-01-25T00:32:27.807261", - "com.redhat.build-host": "ip-10-29-120-186.ec2.internal", - "com.redhat.component": "openstack-nova-api-docker", - "description": "Red Hat OpenStack Platform 12.0 nova-api", - "distribution-scope": "public", - "io.k8s.description": "Red Hat OpenStack Platform 12.0 nova-api", - "io.k8s.display-name": "Red Hat OpenStack Platform 12.0 nova-api", - "io.openshift.tags": "rhosp osp openstack osp-12.0", - "kolla_version": "stable/pike", - "name": "rhosp12/openstack-nova-api", - "release": "20180124.1", - "summary": "Red Hat OpenStack Platform 12.0 nova-api", - "tripleo-common_version": "7.6.3-23-g4891cfe", - "url": "https://access.redhat.com/containers/#/registry.access.redhat.com/rhosp12/openstack-nova-api/images/12.0-20180124.1", - "vcs-ref": "9b31243b7b448eb2fc3b6e2c96935b948f806e98", - "vcs-type": "git", - "vendor": "Red Hat, Inc.", - "version": "12.0", - "version-release": "12.0-20180124.1", - }, - Architecture: "amd64", - Os: "linux", - Layers: []string{ - "sha256:9cadd93b16ff2a0c51ac967ea2abfadfac50cfa3af8b5bf983d89b8f8647f3e4", - "sha256:4aa565ad8b7a87248163ce7dba1dd3894821aac97e846b932ff6b8ef9a8a508a", - "sha256:f576d102e09b9eef0e305aaef705d2d43a11bebc3fd5810a761624bd5e11997e", - "sha256:9e92df2aea7dc0baf5f1f8d509678d6a6306de27ad06513f8e218371938c07a6", - "sha256:62e48e39dc5b30b75a97f05bccc66efbae6058b860ee20a5c9a184b9d5e25788", - "sha256:e623934bca8d1a74f51014256445937714481e49343a31bda2bc5f534748184d", - }, - Env: []string{ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "container=oci", - "KOLLA_BASE_DISTRO=rhel", - "KOLLA_INSTALL_TYPE=binary", - "KOLLA_INSTALL_METATYPE=rhos", - "PS1=$(tput bold)($(printenv KOLLA_SERVICE_NAME))$(tput sgr0)[$(id -un)@$(hostname -s) $(pwd)]$ ", - }, - }, *ii) + assert.Equal(t, "latest", ii.Tag) + assert.Equal(t, &created, ii.Created) + assert.Equal(t, "1.12.6", ii.DockerVersion) + assert.Equal(t, labels, ii.Labels) + assert.Equal(t, "amd64", ii.Architecture) + assert.Equal(t, "linux", ii.Os) + assert.Equal(t, Layers, ii.Layers) + assert.Equal(t, Env, ii.Env) + assert.NotEqual(t, nil, ii.Config) + assert.Equal(t, int64(0), ii.Size) + assert.Equal(t, "", ii.Author) + for _, layer := range ii.LayersDetail { + assert.NotEqual(t, nil, layer) + } + assert.Equal(t, "", ii.Author) + for _, history := range ii.History { + assert.NotEqual(t, nil, history) + } } } diff --git a/internal/image/docker_schema2_test.go b/internal/image/docker_schema2_test.go index 3611fdfdae..56b32ffa8c 100644 --- a/internal/image/docker_schema2_test.go +++ b/internal/image/docker_schema2_test.go @@ -256,30 +256,41 @@ func TestManifestSchema2Inspect(t *testing.T) { ii, err := m.Inspect(context.Background()) require.NoError(t, err) created := time.Date(2016, 9, 23, 23, 20, 45, 789764590, time.UTC) - assert.Equal(t, types.ImageInspectInfo{ - Tag: "", - Created: &created, - DockerVersion: "1.12.1", - Labels: map[string]string{}, - Architecture: "amd64", - Os: "linux", - Layers: []string{ - "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb", - "sha256:1bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c", - "sha256:8f5dc8a4b12c307ac84de90cdd9a7f3915d1be04c9388868ca118831099c67a9", - "sha256:bbd6b22eb11afce63cc76f6bc41042d99f10d6024c96b655dafba930b8d25909", - "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa", - }, - Env: []string{ - "PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HTTPD_PREFIX=/usr/local/apache2", - "HTTPD_VERSION=2.4.23", - "HTTPD_SHA1=5101be34ac4a509b245adb70a56690a84fcc4e7f", - "HTTPD_BZ2_URL=https://www.apache.org/dyn/closer.cgi?action=download&filename=httpd/httpd-2.4.23.tar.bz2", - "HTTPD_ASC_URL=https://www.apache.org/dist/httpd/httpd-2.4.23.tar.bz2.asc", - }, - }, *ii) + layers := []string{ + "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb", + "sha256:1bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c", + "sha256:8f5dc8a4b12c307ac84de90cdd9a7f3915d1be04c9388868ca118831099c67a9", + "sha256:bbd6b22eb11afce63cc76f6bc41042d99f10d6024c96b655dafba930b8d25909", + "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa", + } + env := []string{ + "PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HTTPD_PREFIX=/usr/local/apache2", + "HTTPD_VERSION=2.4.23", + "HTTPD_SHA1=5101be34ac4a509b245adb70a56690a84fcc4e7f", + "HTTPD_BZ2_URL=https://www.apache.org/dyn/closer.cgi?action=download&filename=httpd/httpd-2.4.23.tar.bz2", + "HTTPD_ASC_URL=https://www.apache.org/dist/httpd/httpd-2.4.23.tar.bz2.asc", + } + assert.Equal(t, "", ii.Tag) + assert.Equal(t, &created, ii.Created) + assert.Equal(t, layers, ii.Layers) + + assert.Equal(t, "1.12.1", ii.DockerVersion) + assert.Equal(t, map[string]string{}, ii.Labels) + assert.Equal(t, "amd64", ii.Architecture) + assert.Equal(t, "linux", ii.Os) + assert.Equal(t, layers, ii.Layers) + assert.Equal(t, env, ii.Env) + assert.NotEqual(t, nil, ii.Config) + assert.Equal(t, int64(0), ii.Size) + assert.Equal(t, "", ii.Author) + for _, layer := range ii.LayersDetail { + assert.NotEqual(t, nil, layer) + } + for _, history := range ii.History { + assert.NotEqual(t, nil, history) + } // nil configBlob will trigger an error in m.ConfigBlob() m = manifestSchema2FromComponentsLikeFixture(nil) _, err = m.Inspect(context.Background()) diff --git a/internal/image/oci_test.go b/internal/image/oci_test.go index 07384e01f1..f0f088499a 100644 --- a/internal/image/oci_test.go +++ b/internal/image/oci_test.go @@ -281,30 +281,44 @@ func TestManifestOCI1Inspect(t *testing.T) { ii, err := m.Inspect(context.Background()) require.NoError(t, err) created := time.Date(2016, 9, 23, 23, 20, 45, 789764590, time.UTC) - assert.Equal(t, types.ImageInspectInfo{ - Tag: "", - Created: &created, - DockerVersion: "1.12.1", - Labels: map[string]string{}, - Architecture: "amd64", - Os: "linux", - Layers: []string{ - "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb", - "sha256:1bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c", - "sha256:8f5dc8a4b12c307ac84de90cdd9a7f3915d1be04c9388868ca118831099c67a9", - "sha256:bbd6b22eb11afce63cc76f6bc41042d99f10d6024c96b655dafba930b8d25909", - "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa", - }, - Env: []string{ - "PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HTTPD_PREFIX=/usr/local/apache2", - "HTTPD_VERSION=2.4.23", - "HTTPD_SHA1=5101be34ac4a509b245adb70a56690a84fcc4e7f", - "HTTPD_BZ2_URL=https://www.apache.org/dyn/closer.cgi?action=download&filename=httpd/httpd-2.4.23.tar.bz2", - "HTTPD_ASC_URL=https://www.apache.org/dist/httpd/httpd-2.4.23.tar.bz2.asc", - }, - }, *ii) + layers := []string{ + "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb", + "sha256:1bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c", + "sha256:8f5dc8a4b12c307ac84de90cdd9a7f3915d1be04c9388868ca118831099c67a9", + "sha256:bbd6b22eb11afce63cc76f6bc41042d99f10d6024c96b655dafba930b8d25909", + "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa", + } + + env := []string{ + "PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HTTPD_PREFIX=/usr/local/apache2", + "HTTPD_VERSION=2.4.23", + "HTTPD_SHA1=5101be34ac4a509b245adb70a56690a84fcc4e7f", + "HTTPD_BZ2_URL=https://www.apache.org/dyn/closer.cgi?action=download&filename=httpd/httpd-2.4.23.tar.bz2", + "HTTPD_ASC_URL=https://www.apache.org/dist/httpd/httpd-2.4.23.tar.bz2.asc", + } + + assert.Equal(t, "", ii.Tag) + assert.Equal(t, &created, ii.Created) + assert.Equal(t, layers, ii.Layers) + + assert.Equal(t, "1.12.1", ii.DockerVersion) + assert.Equal(t, map[string]string{}, ii.Labels) + assert.Equal(t, "amd64", ii.Architecture) + assert.Equal(t, "linux", ii.Os) + assert.Equal(t, layers, ii.Layers) + assert.Equal(t, env, ii.Env) + assert.NotEqual(t, nil, ii.Config) + assert.Equal(t, int64(-1), ii.Size) + assert.Equal(t, "", ii.Author) + + for _, layer := range ii.LayersDetail { + assert.NotEqual(t, nil, layer) + } + for _, history := range ii.History { + assert.NotEqual(t, nil, history) + } // nil configBlob will trigger an error in m.ConfigBlob() m = manifestOCI1FromComponentsLikeFixture(nil) _, err = m.Inspect(context.Background()) diff --git a/manifest/docker_schema1.go b/manifest/docker_schema1.go index e1f1fb9d98..651f81411a 100644 --- a/manifest/docker_schema1.go +++ b/manifest/docker_schema1.go @@ -215,6 +215,15 @@ func validateV1ID(id string) error { return nil } +type schema2Image struct { + Schema2V1Image + Parent digest.Digest `json:"parent,omitempty"` + RootFS *Schema2RootFS `json:"rootfs,omitempty"` + History []Schema2History `json:"history,omitempty"` + OSVersion string `json:"os.version,omitempty"` + OSFeatures []string `json:"os.features,omitempty"` +} + // Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration. func (m *Schema1) Inspect(_ func(types.BlobInfo) ([]byte, error)) (*types.ImageInspectInfo, error) { s1 := &Schema2V1Image{} @@ -228,10 +237,36 @@ func (m *Schema1) Inspect(_ func(types.BlobInfo) ([]byte, error)) (*types.ImageI Architecture: s1.Architecture, Os: s1.OS, Layers: layerInfosToStrings(m.LayerInfos()), + LayersDetail: imgInspectLayersFromLayerInfos(m.LayerInfos()), + Author: s1.Author, + Size: s1.Size, } + + diffIDs := []digest.Digest{digest.FromString(s1.ID)} + schema2Config, err := m.ToSchema2Config(diffIDs) + if err != nil { + return nil, err + } + imageConfig := &schema2Image{} + if err := json.Unmarshal(schema2Config, imageConfig); err != nil { + return nil, err + } + i.History = schema2HistoryToV1History(imageConfig.History) if s1.Config != nil { i.Labels = s1.Config.Labels i.Env = s1.Config.Env + i.Config.Env = s1.Config.Env + i.Config.Labels = s1.Config.Labels + i.Config.User = s1.Config.User + i.Config.Volumes = s1.Config.Volumes + i.Config.Entrypoint = s1.Config.Entrypoint + for key, value := range s1.Config.ExposedPorts { + exposedPorts := make(map[string]struct{}) + exposedPorts[string(key)] = value + i.Config.ExposedPorts = exposedPorts + } + i.Config.StopSignal = s1.Config.StopSignal + i.Config.WorkingDir = s1.Config.WorkingDir } return i, nil } diff --git a/manifest/docker_schema2.go b/manifest/docker_schema2.go index e79d0851f2..03ad98e948 100644 --- a/manifest/docker_schema2.go +++ b/manifest/docker_schema2.go @@ -9,6 +9,7 @@ import ( "github.com/containers/image/v5/pkg/strslice" "github.com/containers/image/v5/types" "github.com/opencontainers/go-digest" + v1 "github.com/opencontainers/image-spec/specs-go/v1" ) // Schema2Descriptor is a “descriptor” in docker/distribution schema 2. @@ -215,6 +216,31 @@ func (m *Schema2) LayerInfos() []LayerInfo { return blobs } +// imgInspectLayersFromLayerInfos converts a list of layer infos, presumably obtained from a Manifest.LayerInfos() +// method call, into a format suitable for inclusion in a types.ImageInspectInfo structure. +func imgInspectLayersFromLayerInfos(infos []LayerInfo) []types.LayerDetail { + layers := make([]types.LayerDetail, len(infos)) + for i, info := range infos { + layers[i].MIMEType = info.MediaType + layers[i].Digest = info.Digest + layers[i].Size = info.Size + layers[i].Annotations = info.Annotations + } + return layers +} + +func schema2HistoryToV1History(history []Schema2History) []v1.History { + v1History := make([]v1.History, len(history)) + for index, value := range history { + v1History[index].Created = &value.Created + v1History[index].Author = value.Author + v1History[index].CreatedBy = value.CreatedBy + v1History[index].Comment = value.Comment + v1History[index].EmptyLayer = value.EmptyLayer + } + return v1History +} + var schema2CompressionMIMETypeSets = []compressionMIMETypeSet{ { mtsUncompressed: DockerV2Schema2ForeignLayerMediaType, @@ -279,10 +305,26 @@ func (m *Schema2) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*t Variant: s2.Variant, Os: s2.OS, Layers: layerInfosToStrings(m.LayerInfos()), + LayersDetail: imgInspectLayersFromLayerInfos(m.LayerInfos()), + History: schema2HistoryToV1History(s2.History), + Author: s2.Author, + Size: s2.Size, } if s2.Config != nil { i.Labels = s2.Config.Labels i.Env = s2.Config.Env + i.Config.Env = s2.Config.Env + i.Config.Labels = s2.Config.Labels + i.Config.User = s2.Config.User + i.Config.Volumes = s2.Config.Volumes + i.Config.Entrypoint = s2.Config.Entrypoint + for key, value := range s2.Config.ExposedPorts { + exposedPorts := make(map[string]struct{}) + exposedPorts[string(key)] = value + i.Config.ExposedPorts = exposedPorts + } + i.Config.StopSignal = s2.Config.StopSignal + i.Config.WorkingDir = s2.Config.WorkingDir } return i, nil } diff --git a/manifest/oci.go b/manifest/oci.go index fc325009ce..0ac1bdf8b4 100644 --- a/manifest/oci.go +++ b/manifest/oci.go @@ -212,6 +212,7 @@ func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*type if err := json.Unmarshal(config, d1); err != nil { return nil, err } + layerInfos := m.LayerInfos() i := &types.ImageInspectInfo{ Tag: "", Created: v1.Created, @@ -219,8 +220,13 @@ func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*type Labels: v1.Config.Labels, Architecture: v1.Architecture, Os: v1.OS, - Layers: layerInfosToStrings(m.LayerInfos()), + Layers: layerInfosToStrings(layerInfos), + LayersDetail: imgInspectLayersFromLayerInfos(layerInfos), Env: v1.Config.Env, + History: v1.History, + Author: v1.Author, + Size: -1, + Config: v1.Config, } return i, nil } diff --git a/types/types.go b/types/types.go index dcff8caf76..0d11f2b941 100644 --- a/types/types.go +++ b/types/types.go @@ -466,6 +466,34 @@ type ImageInspectInfo struct { Os string Layers []string Env []string + LayersDetail []LayerDetail + Author string + // Size is the total size of the image including all layers it is composed of + Size int64 + Config v1.ImageConfig + History []v1.History +} + +type Schema2History struct { + // Created is the timestamp at which the image was created + Created time.Time `json:"created"` + // Author is the name of the author that was specified when committing the image + Author string `json:"author,omitempty"` + // CreatedBy keeps the Dockerfile command used while building the image + CreatedBy string `json:"created_by,omitempty"` + // Comment is the commit message that was set when committing the image + Comment string `json:"comment,omitempty"` + // EmptyLayer is set to true if this history item did not generate a + // layer. Otherwise, the history item is associated with the next + // layer in the RootFS section. + EmptyLayer bool `json:"empty_layer,omitempty"` +} + +type LayerDetail struct { + MIMEType string + Digest digest.Digest + Size int64 + Annotations map[string]string } // DockerAuthConfig contains authorization information for connecting to a registry.