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

Image pruning fixes #16717

Merged
merged 3 commits into from
Oct 10, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
47 changes: 29 additions & 18 deletions pkg/image/prune/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ var _ Pruner = &pruner{}
//
// Also automatically remove any image layer that is no longer referenced by any
// images.
func NewPruner(options PrunerOptions) Pruner {
func NewPruner(options PrunerOptions) (Pruner, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

godoc

glog.V(1).Infof("Creating image pruner with keepYoungerThan=%v, keepTagRevisions=%s, pruneOverSizeLimit=%s, allImages=%s",
options.KeepYoungerThan, getValue(options.KeepTagRevisions), getValue(options.PruneOverSizeLimit), getValue(options.AllImages))

Expand All @@ -233,7 +233,9 @@ func NewPruner(options PrunerOptions) Pruner {
addImageStreamsToGraph(g, options.Streams, options.LimitRanges, algorithm)
addPodsToGraph(g, options.Pods, algorithm)
addReplicationControllersToGraph(g, options.RCs)
addBuildConfigsToGraph(g, options.BCs)
if err := addBuildConfigsToGraph(g, options.BCs); err != nil {
return nil, err
}
addBuildsToGraph(g, options.Builds)
addDeploymentConfigsToGraph(g, options.DCs)

Expand All @@ -242,7 +244,7 @@ func NewPruner(options PrunerOptions) Pruner {
algorithm: algorithm,
registryClient: options.RegistryClient,
registryURL: options.RegistryURL,
}
}, nil
}

func getValue(option interface{}) string {
Expand Down Expand Up @@ -484,13 +486,16 @@ func addDeploymentConfigsToGraph(g graph.Graph, dcs *deployapi.DeploymentConfigL
// addBuildConfigsToGraph adds build configs to the graph.
//
// Edges are added to the graph from each build config to the image specified by its strategy.from.
func addBuildConfigsToGraph(g graph.Graph, bcs *buildapi.BuildConfigList) {
func addBuildConfigsToGraph(g graph.Graph, bcs *buildapi.BuildConfigList) error {
for i := range bcs.Items {
bc := &bcs.Items[i]
glog.V(4).Infof("Examining BuildConfig %s", getName(bc))
bcNode := buildgraph.EnsureBuildConfigNode(g, bc)
addBuildStrategyImageReferencesToGraph(g, bc.Spec.Strategy, bcNode)
if err := addBuildStrategyImageReferencesToGraph(g, bc.Spec.Strategy, bcNode); err != nil {
return err
}
}
return nil
}

// addBuildsToGraph adds builds to the graph.
Expand All @@ -511,11 +516,11 @@ func addBuildsToGraph(g graph.Graph, builds *buildapi.BuildList) {
// Edges are added to the graph from each predecessor (build or build config)
// to the image specified by strategy.from, as long as the image is managed by
// OpenShift.
func addBuildStrategyImageReferencesToGraph(g graph.Graph, strategy buildapi.BuildStrategy, predecessor gonum.Node) {
func addBuildStrategyImageReferencesToGraph(g graph.Graph, strategy buildapi.BuildStrategy, predecessor gonum.Node) error {
from := buildapi.GetInputReference(strategy)
if from == nil {
glog.V(4).Infof("Unable to determine 'from' reference - skipping")
return
return nil
}

glog.V(4).Infof("Examining build strategy with from: %#v", from)
Expand All @@ -527,29 +532,31 @@ func addBuildStrategyImageReferencesToGraph(g graph.Graph, strategy buildapi.Bui
_, id, err := imageapi.ParseImageStreamImageName(from.Name)
if err != nil {
glog.V(2).Infof("Error parsing ImageStreamImage name %q: %v - skipping", from.Name, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skipping - this is not true anymore

return
return err
}
imageID = id
case "DockerImage":
ref, err := imageapi.ParseDockerImageReference(from.Name)
if err != nil {
glog.V(2).Infof("Error parsing DockerImage name %q: %v - skipping", from.Name, err)
return
return err
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone manages to create build config with corrupted image reference, it will prevent the pruner from running. Is this really such an important error?

If it is, let's add some flag (e.g. --force) marching over small errors, crippled references and dead men.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@miminar You can not create such strategy:

# buildconfigs "cakephp-ex" was not valid:
# * spec.strategy.sourceStrategy.from.name: Invalid value: "foo:bar:baz": not a valid Docker pull specification: invalid reference format

Also even if I add such a option, then it will need to specify this option constantly until broken strategy is removed.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the prior docker/distribution rebases made parsing of references stricter. I can easily imagine really old build strategy with a relaxed reference value. I can also imagine this parser becoming even stricter.

Also even if I add such a option, then it will need to specify this option constantly until broken strategy is removed.

If the error is printed, the admin will have the opportunity to act and tune/delete the object. Make sure to include namespace/name of the build config.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@miminar Done

}
imageID = ref.ID
default:
return
return nil
}

glog.V(4).Infof("Looking for image %q in graph", imageID)
imageNode := imagegraph.FindImage(g, imageID)
if imageNode == nil {
glog.V(4).Infof("Unable to find image %q in graph - skipping", imageID)
return
return nil
}

glog.V(4).Infof("Adding edge from %v to %v", predecessor, imageNode)
g.AddEdge(predecessor, imageNode, ReferencedImageEdgeKind)

return nil
}

// getImageNodes returns only nodes of type ImageNode.
Expand Down Expand Up @@ -766,27 +773,31 @@ func (p *pruner) Prune(

prunableImageNodes, prunableImageIDs := calculatePrunableImages(p.g, imageNodes, p.algorithm)

errs := []error{}
errs = append(errs, pruneStreams(p.g, prunableImageNodes, streamPruner)...)
var errs []error

errs = pruneStreams(p.g, prunableImageNodes, streamPruner)
// if namespace is specified prune only ImageStreams and nothing more
if len(p.algorithm.namespace) > 0 {
// if we have any errors after ImageStreams pruning this may mean that
// we still have references to images.
if len(p.algorithm.namespace) > 0 || len(errs) > 0 {
return kerrors.NewAggregate(errs)
}

graphWithoutPrunableImages := subgraphWithoutPrunableImages(p.g, prunableImageIDs)
prunableComponents := calculatePrunableImageComponents(graphWithoutPrunableImages)

errs = append(errs, pruneImageComponents(p.g, p.registryClient, p.registryURL, prunableComponents, layerLinkPruner)...)
errs = append(errs, pruneBlobs(p.g, p.registryClient, p.registryURL, prunableComponents, blobPruner)...)
errs = append(errs, pruneManifests(p.g, p.registryClient, p.registryURL, prunableImageNodes, manifestPruner)...)

if len(errs) > 0 {
// If we had any errors removing image references from image streams or deleting
// layers, blobs, or manifest data from the registry, stop here and don't
// delete any images. This way, you can rerun prune and retry things that failed.
// If we had any errors deleting layers, blobs, or manifest data from the registry,
// stop here and don't delete any images. This way, you can rerun prune and retry
// things that failed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to get smarter about this and still prune the images for which we successfully pruned all the layers/blobs/manifests.

return kerrors.NewAggregate(errs)
}

errs = append(errs, pruneImages(p.g, prunableImageNodes, imagePruner)...)
errs = pruneImages(p.g, prunableImageNodes, imagePruner)
return kerrors.NewAggregate(errs)
}

Expand Down
5 changes: 4 additions & 1 deletion pkg/oc/admin/prune/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,10 @@ func (o PruneImagesOptions) Run() error {
if o.Namespace != metav1.NamespaceAll {
options.Namespace = o.Namespace
}
pruner := prune.NewPruner(options)
pruner, err := prune.NewPruner(options)
if err != nil {
return err
}

w := tabwriter.NewWriter(o.Out, 10, 4, 3, ' ', 0)
defer w.Flush()
Expand Down