// makeImageStreamTagAdmissionUsageFunc returns a function that computes a resource usage for given image // stream tag during admission. func makeImageStreamTagAdmissionUsageFunc(isNamespacer osclient.ImageStreamsNamespacer) generic.UsageFunc { return func(object runtime.Object) kapi.ResourceList { ist, ok := object.(*imageapi.ImageStreamTag) if !ok { return kapi.ResourceList{} } res := map[kapi.ResourceName]resource.Quantity{ imageapi.ResourceImageStreams: *resource.NewQuantity(0, resource.BinarySI), } isName, _, err := imageapi.ParseImageStreamTagName(ist.Name) if err != nil { utilruntime.HandleError(err) return kapi.ResourceList{} } is, err := isNamespacer.ImageStreams(ist.Namespace).Get(isName) if err != nil { if !kerrors.IsNotFound(err) { utilruntime.HandleError(fmt.Errorf("failed to get image stream %s/%s: %v", ist.Namespace, isName, err)) } } if is == nil { res[imageapi.ResourceImageStreams] = *resource.NewQuantity(1, resource.BinarySI) } return res } }
// DeletingImageStreamPruneFunc returns an ImageStreamPruneFunc that deletes the imageStream. func DeletingImageStreamPruneFunc(streams client.ImageStreamsNamespacer) ImageStreamPruneFunc { return func(stream *imageapi.ImageStream, image *imageapi.Image) (*imageapi.ImageStream, error) { glog.V(4).Infof("Checking if ImageStream %s/%s has references to image in status.tags", stream.Namespace, stream.Name) for tag, history := range stream.Status.Tags { glog.V(4).Infof("Checking tag %q", tag) newHistory := imageapi.TagEventList{} for i, tagEvent := range history.Items { glog.V(4).Infof("Checking tag event %d with image %q", i, tagEvent.Image) if tagEvent.Image != image.Name { glog.V(4).Infof("Tag event doesn't match deleting image - keeping") newHistory.Items = append(newHistory.Items, tagEvent) } } stream.Status.Tags[tag] = newHistory } glog.V(4).Infof("Updating ImageStream %s/%s", stream.Namespace, stream.Name) glog.V(5).Infof("Updated stream: %#v", stream) updatedStream, err := streams.ImageStreams(stream.Namespace).UpdateStatus(stream) if err != nil { return nil, err } return updatedStream, nil } }
// makeImageStreamImportAdmissionUsageFunc retuns a function for computing a usage of an image stream import. func makeImageStreamImportAdmissionUsageFunc(isNamespacer osclient.ImageStreamsNamespacer) generic.UsageFunc { return func(object runtime.Object) kapi.ResourceList { isi, ok := object.(*imageapi.ImageStreamImport) if !ok { return kapi.ResourceList{} } usage := map[kapi.ResourceName]resource.Quantity{ imageapi.ResourceImageStreams: *resource.NewQuantity(0, resource.DecimalSI), } if !isi.Spec.Import || (len(isi.Spec.Images) == 0 && isi.Spec.Repository == nil) { return usage } is, err := isNamespacer.ImageStreams(isi.Namespace).Get(isi.Name) if err != nil { if !kerrors.IsNotFound(err) { utilruntime.HandleError(fmt.Errorf("failed to list image streams: %v", err)) } } if is == nil { usage[imageapi.ResourceImageStreams] = *resource.NewQuantity(1, resource.DecimalSI) } return usage } }
// NewImageStreamEvaluator computes resource usage of ImageStreams. Instantiating this is necessary for // resource quota admission controller to properly work on image stream related objects. func NewImageStreamEvaluator(isNamespacer osclient.ImageStreamsNamespacer) kquota.Evaluator { allResources := []kapi.ResourceName{ imageapi.ResourceImageStreams, } return &generic.GenericEvaluator{ Name: imageStreamEvaluatorName, InternalGroupKind: imageapi.Kind("ImageStream"), InternalOperationResources: map[admission.Operation][]kapi.ResourceName{ admission.Create: allResources, }, MatchedResourceNames: allResources, MatchesScopeFunc: generic.MatchesNoScopeFunc, ConstraintsFunc: generic.ObjectCountConstraintsFunc(imageapi.ResourceImageStreams), UsageFunc: generic.ObjectCountUsageFunc(imageapi.ResourceImageStreams), ListFuncByNamespace: func(namespace string, options kapi.ListOptions) (runtime.Object, error) { return isNamespacer.ImageStreams(namespace).List(options) }, } }
// processTriggers will go over all deployment triggers that require processing and update // the deployment config accordingly. This contains the work that the image change controller // had been doing up to the point we got the /instantiate endpoint. func processTriggers(config *deployapi.DeploymentConfig, isn client.ImageStreamsNamespacer, force bool) error { errs := []error{} // Process any image change triggers. for _, trigger := range config.Spec.Triggers { if trigger.Type != deployapi.DeploymentTriggerOnImageChange { continue } params := trigger.ImageChangeParams // Forced deployments should always try to resolve the images in the template. // On the other hand, paused deployments or non-automatic triggers shouldn't. if !force && (config.Spec.Paused || !params.Automatic) { continue } // Tag references are already validated name, tag, _ := imageapi.SplitImageStreamTag(params.From.Name) stream, err := isn.ImageStreams(params.From.Namespace).Get(name) if err != nil { if !errors.IsNotFound(err) { errs = append(errs, err) } continue } // Find the latest tag event for the trigger reference. latestEvent := imageapi.LatestTaggedImage(stream, tag) if latestEvent == nil { continue } // Ensure a change occurred latestRef := latestEvent.DockerImageReference if len(latestRef) == 0 || latestRef == params.LastTriggeredImage { continue } // Update containers names := sets.NewString(params.ContainerNames...) for i := range config.Spec.Template.Spec.Containers { container := &config.Spec.Template.Spec.Containers[i] if !names.Has(container.Name) { continue } if container.Image != latestRef { // Update the image container.Image = latestRef // Log the last triggered image ID params.LastTriggeredImage = latestRef } } } if err := utilerrors.NewAggregate(errs); err != nil { return errors.NewInternalError(err) } return nil }