Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update commands with resource builders to use external versions #20400

Closed
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 125 additions & 78 deletions pkg/image/util/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,20 @@ import (
"github.com/golang/glog"
godigest "github.com/opencontainers/go-digest"

imagev1 "github.com/openshift/api/image/v1"
imageapi "github.com/openshift/origin/pkg/image/apis/image"
"github.com/openshift/origin/pkg/image/apis/image/docker10"
)

func fillImageLayers(image *imageapi.Image, manifest docker10.DockerImageManifest) error {
if len(image.DockerImageLayers) != 0 {
// DockerImageLayers is already filled by the registry.
return nil
}

func getImageLayers(manifest docker10.DockerImageManifest) ([]imageapi.ImageLayer, error) {
var imageLayers []imageapi.ImageLayer
switch manifest.SchemaVersion {
case 1:
if len(manifest.History) != len(manifest.FSLayers) {
return fmt.Errorf("the image %s (%s) has mismatched history and fslayer cardinality (%d != %d)", image.Name, image.DockerImageReference, len(manifest.History), len(manifest.FSLayers))
return nil, fmt.Errorf("mismatched history and fslayer cardinality (%d != %d)", len(manifest.History), len(manifest.FSLayers))
}

image.DockerImageLayers = make([]imageapi.ImageLayer, len(manifest.FSLayers))
imageLayers = make([]imageapi.ImageLayer, len(manifest.FSLayers))
for i, obj := range manifest.History {
layer := manifest.FSLayers[i]

Expand All @@ -42,29 +39,80 @@ func fillImageLayers(image *imageapi.Image, manifest docker10.DockerImageManifes
// in order from the oldest to the youngest.
revidx := (len(manifest.History) - 1) - i // n-1, n-2, ..., 1, 0

image.DockerImageLayers[revidx].Name = layer.DockerBlobSum
image.DockerImageLayers[revidx].LayerSize = size.Size
image.DockerImageLayers[revidx].MediaType = schema1.MediaTypeManifestLayer
imageLayers[revidx].Name = layer.DockerBlobSum
imageLayers[revidx].LayerSize = size.Size
imageLayers[revidx].MediaType = schema1.MediaTypeManifestLayer
}
case 2:
// The layer list is ordered starting from the base image (opposite order of schema1).
// So, we do not need to change the order of layers.
image.DockerImageLayers = make([]imageapi.ImageLayer, len(manifest.Layers))
imageLayers = make([]imageapi.ImageLayer, len(manifest.Layers))
for i, layer := range manifest.Layers {
image.DockerImageLayers[i].Name = layer.Digest
image.DockerImageLayers[i].LayerSize = layer.Size
image.DockerImageLayers[i].MediaType = layer.MediaType
imageLayers[i].Name = layer.Digest
imageLayers[i].LayerSize = layer.Size
imageLayers[i].MediaType = layer.MediaType
}
default:
return fmt.Errorf("unrecognized Docker image manifest schema %d for %q (%s)", manifest.SchemaVersion, image.Name, image.DockerImageReference)
return nil, fmt.Errorf("unrecognized Docker image manifest schema %d", manifest.SchemaVersion)
}

if image.Annotations == nil {
image.Annotations = map[string]string{}
return imageLayers, nil
}

// reorderImageLayers mutates the given image. It reorders the layers in ascending order.
// Ascending order matches the order of layers in schema 2. Schema 1 has reversed (descending) order of layers.
func reorderImageLayers(imageLayers []imageapi.ImageLayer, layersOrder, imageManifestMediaType string) bool {
if imageLayers == nil || len(imageLayers) == 0 {
return false
}
image.Annotations[imageapi.DockerImageLayersOrderAnnotation] = imageapi.DockerImageLayersOrderAscending

return nil
if layersOrder == "" {
switch imageManifestMediaType {
case schema1.MediaTypeManifest, schema1.MediaTypeSignedManifest:
layersOrder = imageapi.DockerImageLayersOrderAscending
case schema2.MediaTypeManifest:
layersOrder = imageapi.DockerImageLayersOrderDescending
default:
return false
}
}

if layersOrder == imageapi.DockerImageLayersOrderDescending {
// reverse order of the layers (lowest = 0, highest = i)
for i, j := 0, len(imageLayers)-1; i < j; i, j = i+1, j-1 {
imageLayers[i], imageLayers[j] = imageLayers[j], imageLayers[i]
}
}

return true
}

func convertImageLayers(imageLayers []imagev1.ImageLayer) []imageapi.ImageLayer {
if imageLayers == nil {
return nil
}

result := make([]imageapi.ImageLayer, len(imageLayers))
for i := range imageLayers {
result[i].MediaType = imageLayers[i].MediaType
result[i].Name = imageLayers[i].Name
result[i].LayerSize = imageLayers[i].LayerSize
}
return result
}

func GetImageMetadata(image *imagev1.Image) (imageapi.DockerImage, error) {
if len(image.DockerImageManifest) == 0 {
return imageapi.DockerImage{}, nil
}

imageLayers := convertImageLayers(image.DockerImageLayers)
reorderImageLayers(imageLayers, image.Annotations[imageapi.DockerImageLayersOrderAnnotation], image.DockerImageManifestMediaType)

_, imageMetadata, _, _, err := getImageMetadata(image.Name, image.DockerImageReference,
image.DockerImageManifest, image.DockerImageConfig, imageLayers)
return imageMetadata, err

}

// ImageWithMetadata mutates the given image. It parses raw DockerImageManifest data stored in the image and
Expand All @@ -74,110 +122,109 @@ func ImageWithMetadata(image *imageapi.Image) error {
return nil
}

ReorderImageLayers(image)
if ok := reorderImageLayers(image.DockerImageLayers,
image.Annotations[imageapi.DockerImageLayersOrderAnnotation], image.DockerImageManifestMediaType); ok {
if image.Annotations == nil {
image.Annotations = map[string]string{}
}
image.Annotations[imageapi.DockerImageLayersOrderAnnotation] = imageapi.DockerImageLayersOrderAscending
}

if len(image.DockerImageLayers) > 0 && image.DockerImageMetadata.Size > 0 && len(image.DockerImageManifestMediaType) > 0 {
glog.V(5).Infof("Image metadata already filled for %s", image.Name)
return nil
}
imageManifestMediaType, imageMetadata, imageLayers, orderAscending, err := getImageMetadata(image.Name, image.DockerImageReference,
image.DockerImageManifest, image.DockerImageConfig, image.DockerImageLayers)
if err != nil {
return err
}
image.DockerImageManifestMediaType = imageManifestMediaType
image.DockerImageMetadata = imageMetadata
image.DockerImageLayers = imageLayers
if orderAscending {
if image.Annotations == nil {
image.Annotations = map[string]string{}
}
image.Annotations[imageapi.DockerImageLayersOrderAnnotation] = imageapi.DockerImageLayersOrderAscending
}

return nil
}

func getImageMetadata(imageName, imageReference, imageManifest, imageConfig string,
imageLayers []imageapi.ImageLayer) (string, imageapi.DockerImage, []imageapi.ImageLayer, bool, error) {
manifest := docker10.DockerImageManifest{}
if err := json.Unmarshal([]byte(image.DockerImageManifest), &manifest); err != nil {
return err
if err := json.Unmarshal([]byte(imageManifest), &manifest); err != nil {
return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, err
}

err := fillImageLayers(image, manifest)
if err != nil {
return err
var err error
var orderAscending bool
if len(imageLayers) == 0 {
imageLayers, err = getImageLayers(manifest)
if err != nil {
return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, fmt.Errorf("the image %s (%s) failed reading layers: %v", imageName, imageReference, err)
}
orderAscending = true
}

var imageManifestMediaType string
var imageMetadata imageapi.DockerImage
switch manifest.SchemaVersion {
case 1:
image.DockerImageManifestMediaType = schema1.MediaTypeManifest
imageManifestMediaType = schema1.MediaTypeManifest

if len(manifest.History) == 0 {
// It should never have an empty history, but just in case.
return fmt.Errorf("the image %s (%s) has a schema 1 manifest, but it doesn't have history", image.Name, image.DockerImageReference)
return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, fmt.Errorf("the image %s (%s) has a schema 1 manifest, but it doesn't have history", imageName, imageReference)
}

v1Metadata := docker10.DockerV1CompatibilityImage{}
if err := json.Unmarshal([]byte(manifest.History[0].DockerV1Compatibility), &v1Metadata); err != nil {
return err
return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, err
}

if err := imageapi.Convert_compatibility_to_api_DockerImage(&v1Metadata, &image.DockerImageMetadata); err != nil {
return err
if err := imageapi.Convert_compatibility_to_api_DockerImage(&v1Metadata, &imageMetadata); err != nil {
return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, err
}
case 2:
image.DockerImageManifestMediaType = schema2.MediaTypeManifest
imageManifestMediaType = schema2.MediaTypeManifest

if len(image.DockerImageConfig) == 0 {
return fmt.Errorf("dockerImageConfig must not be empty for manifest schema 2")
if len(imageConfig) == 0 {
return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, fmt.Errorf("dockerImageConfig must not be empty for manifest schema 2")
}

config := docker10.DockerImageConfig{}
if err := json.Unmarshal([]byte(image.DockerImageConfig), &config); err != nil {
return fmt.Errorf("failed to parse dockerImageConfig: %v", err)
if err := json.Unmarshal([]byte(imageConfig), &config); err != nil {
return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, fmt.Errorf("failed to parse dockerImageConfig: %v", err)
}

if err := imageapi.Convert_imageconfig_to_api_DockerImage(&config, &image.DockerImageMetadata); err != nil {
return err
if err := imageapi.Convert_imageconfig_to_api_DockerImage(&config, &imageMetadata); err != nil {
return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, err
}
image.DockerImageMetadata.ID = manifest.Config.Digest
imageMetadata.ID = manifest.Config.Digest

default:
return fmt.Errorf("unrecognized Docker image manifest schema %d for %q (%s)", manifest.SchemaVersion, image.Name, image.DockerImageReference)
return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, fmt.Errorf("unrecognized Docker image manifest schema %d for %q (%s)", manifest.SchemaVersion, imageName, imageReference)
}

layerSet := sets.NewString()
if manifest.SchemaVersion == 2 {
layerSet.Insert(manifest.Config.Digest)
image.DockerImageMetadata.Size = int64(len(image.DockerImageConfig))
imageMetadata.Size = int64(len(imageConfig))
} else {
image.DockerImageMetadata.Size = 0
imageMetadata.Size = 0
}
for _, layer := range image.DockerImageLayers {
for _, layer := range imageLayers {
if layerSet.Has(layer.Name) {
continue
}
layerSet.Insert(layer.Name)
image.DockerImageMetadata.Size += layer.LayerSize
}

return nil
}

// ReorderImageLayers mutates the given image. It reorders the layers in ascending order.
// Ascending order matches the order of layers in schema 2. Schema 1 has reversed (descending) order of layers.
func ReorderImageLayers(image *imageapi.Image) {
if len(image.DockerImageLayers) == 0 {
return
}

layersOrder, ok := image.Annotations[imageapi.DockerImageLayersOrderAnnotation]
if !ok {
switch image.DockerImageManifestMediaType {
case schema1.MediaTypeManifest, schema1.MediaTypeSignedManifest:
layersOrder = imageapi.DockerImageLayersOrderAscending
case schema2.MediaTypeManifest:
layersOrder = imageapi.DockerImageLayersOrderDescending
default:
return
}
}

if layersOrder == imageapi.DockerImageLayersOrderDescending {
// reverse order of the layers (lowest = 0, highest = i)
for i, j := 0, len(image.DockerImageLayers)-1; i < j; i, j = i+1, j-1 {
image.DockerImageLayers[i], image.DockerImageLayers[j] = image.DockerImageLayers[j], image.DockerImageLayers[i]
}
}

if image.Annotations == nil {
image.Annotations = map[string]string{}
imageMetadata.Size += layer.LayerSize
}

image.Annotations[imageapi.DockerImageLayersOrderAnnotation] = imageapi.DockerImageLayersOrderAscending
return imageManifestMediaType, imageMetadata, imageLayers, orderAscending, nil
}

// ManifestMatchesImage returns true if the provided manifest matches the name of the image.
Expand Down
Loading