func convert_v1_BuildOutput_To_api_BuildOutput(in *BuildOutput, out *newer.BuildOutput, s conversion.Scope) error { if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil { return err } if in.To != nil && in.To.Kind == "ImageStreamTag" { name, tag, ok := imageapi.SplitImageStreamTag(in.To.Name) if !ok { return fmt.Errorf("ImageStreamTag object references must be in the form <name>:<tag>: %s", in.To.Name) } out.To.Kind = "ImageStream" out.To.Name = name out.Tag = tag return nil } if in.To != nil && in.To.Kind == "DockerImage" { out.To = nil if ref, err := imageapi.ParseDockerImageReference(in.To.Name); err == nil { out.Tag = ref.Tag ref.Tag = "" out.DockerImageReference = ref.String() } else { out.DockerImageReference = in.To.Name } } return nil }
func convert_v1beta3_DeploymentTriggerImageChangeParams_To_api_DeploymentTriggerImageChangeParams(in *DeploymentTriggerImageChangeParams, out *newer.DeploymentTriggerImageChangeParams, s conversion.Scope) error { out.Automatic = in.Automatic out.ContainerNames = make([]string, len(in.ContainerNames)) copy(out.ContainerNames, in.ContainerNames) out.LastTriggeredImage = in.LastTriggeredImage if err := s.Convert(&in.From, &out.From, 0); err != nil { return err } switch in.From.Kind { case "DockerImage": ref, err := imageapi.ParseDockerImageReference(in.From.Name) if err != nil { return err } out.Tag = ref.Tag ref.Tag, ref.ID = "", "" out.RepositoryName = ref.String() case "ImageStreamTag": name, tag, ok := imageapi.SplitImageStreamTag(in.From.Name) if !ok { return fmt.Errorf("ImageStreamTag object references must be in the form <name>:<tag>: %s", in.From.Name) } out.From.Kind = "ImageStream" out.From.Name = name out.Tag = tag } return nil }
// DockerRepository adds the named Docker repository tag reference to the graph if it does // not already exist. If the reference is invalid, the Name field of the graph will be // used directly. func DockerRepository(g MutableUniqueGraph, name, tag string) graph.Node { ref, err := image.ParseDockerImageReference(name) if err == nil { if len(tag) != 0 { ref.Tag = tag } if len(ref.Tag) == 0 { ref.Tag = image.DefaultImageTag } if len(ref.Registry) == 0 { ref.Registry = "docker.io" } if len(ref.Namespace) == 0 { ref.Namespace = image.DockerDefaultNamespace } // TODO: canonicalize name = ref.String() } else { ref = image.DockerImageReference{Name: name} } return EnsureUnique(g, UniqueName(fmt.Sprintf("%d|%s", DockerRepositoryGraphKind, name)), func(node Node) graph.Node { return &DockerImageRepositoryNode{node, ref} }, ) }
// ValidateImageStreamMapping tests required fields for an ImageStreamMapping. func ValidateImageStreamMapping(mapping *api.ImageStreamMapping) fielderrors.ValidationErrorList { result := fielderrors.ValidationErrorList{} result = append(result, validation.ValidateObjectMeta(&mapping.ObjectMeta, true, oapi.MinimalNameRequirements).Prefix("metadata")...) hasRepository := len(mapping.DockerImageRepository) != 0 hasName := len(mapping.Name) != 0 switch { case hasRepository: if _, err := api.ParseDockerImageReference(mapping.DockerImageRepository); err != nil { result = append(result, fielderrors.NewFieldInvalid("dockerImageRepository", mapping.DockerImageRepository, err.Error())) } case hasName: default: result = append(result, fielderrors.NewFieldRequired("name")) result = append(result, fielderrors.NewFieldRequired("dockerImageRepository")) } if ok, msg := validation.ValidateNamespaceName(mapping.Namespace, false); !ok { result = append(result, fielderrors.NewFieldInvalid("namespace", mapping.Namespace, msg)) } if len(mapping.Tag) == 0 { result = append(result, fielderrors.NewFieldRequired("tag")) } if errs := ValidateImage(&mapping.Image).Prefix("image"); len(errs) != 0 { result = append(result, errs...) } return result }
// FromName generates an ImageRef from a given name func (g *imageRefGenerator) FromName(name string) (*ImageRef, error) { ref, err := imageapi.ParseDockerImageReference(name) if err != nil { return nil, err } return &ImageRef{ DockerImageReference: ref, }, nil }
// Resolve will attempt to find an imagestream with a name that matches the passed in value func (r ImageStreamResolver) Resolve(value string) (*ComponentMatch, error) { ref, err := imageapi.ParseDockerImageReference(value) if err != nil || len(ref.Registry) != 0 { return nil, fmt.Errorf("image repositories must be of the form [<namespace>/]<name>[:<tag>|@<digest>]") } namespaces := r.Namespaces if len(ref.Namespace) != 0 { namespaces = []string{ref.Namespace} } searchTag := ref.Tag if len(searchTag) == 0 { searchTag = imageapi.DefaultImageTag } for _, namespace := range namespaces { glog.V(4).Infof("checking ImageStream %s/%s with ref %q", namespace, ref.Name, searchTag) repo, err := r.Client.ImageStreams(namespace).Get(ref.Name) if err != nil { if errors.IsNotFound(err) || errors.IsForbidden(err) { continue } return nil, err } ref.Namespace = namespace latest := imageapi.LatestTaggedImage(repo, searchTag) if latest == nil { // continue searching in the next namespace glog.V(2).Infof("no image recorded for %s/%s:%s", repo.Namespace, repo.Name, searchTag) continue } imageStreamImage, err := r.ImageStreamImages.ImageStreamImages(namespace).Get(ref.Name, latest.Image) if err != nil { if errors.IsNotFound(err) { // continue searching in the next namespace glog.V(2).Infof("tag %q is set, but image %q has been removed", searchTag, latest.Image) continue } return nil, err } imageData := imageStreamImage.Image ref.Registry = "" return &ComponentMatch{ Value: ref.String(), Argument: fmt.Sprintf("--image=%q", ref.String()), Name: ref.Name, Description: fmt.Sprintf("Image repository %s (tag %q) in project %s, tracks %q", repo.Name, searchTag, repo.Namespace, repo.Status.DockerImageRepository), Builder: IsBuilderImage(&imageData.DockerImageMetadata), Score: 0, ImageStream: repo, Image: &imageData.DockerImageMetadata, ImageTag: searchTag, }, nil } return nil, ErrNoMatch{value: value} }
// BuildConfig adds a graph node for the specific build config if it does not exist, // and will link the build config to other nodes for the images and source repositories // it depends on. func BuildConfig(g MutableUniqueGraph, config *build.BuildConfig) graph.Node { node, found := g.FindOrCreate( UniqueName(fmt.Sprintf("%d|%s/%s", BuildConfigGraphKind, config.Namespace, config.Name)), func(node Node) graph.Node { return &BuildConfigNode{ Node: node, BuildConfig: config, } }, ) if found { return node } output := config.Parameters.Output to := output.To switch { case to != nil && len(to.Name) > 0: out := ImageStreamTag(g, defaultNamespace(to.Namespace, config.Namespace), to.Name, output.Tag) g.AddEdge(node, out, BuildOutputGraphEdgeKind) case len(output.DockerImageReference) > 0: out := DockerRepository(g, output.DockerImageReference, output.Tag) g.AddEdge(node, out, BuildOutputGraphEdgeKind) } if in, ok := SourceRepository(g, config.Parameters.Source); ok { g.AddEdge(in, node, BuildInputGraphEdgeKind) } from := buildutil.GetImageStreamForStrategy(config.Parameters.Strategy) if from != nil { switch from.Kind { case "DockerImage": if ref, err := image.ParseDockerImageReference(from.Name); err == nil { tag := ref.Tag ref.Tag = "" in := DockerRepository(g, ref.String(), tag) g.AddEdge(in, node, BuildInputImageGraphEdgeKind) } case "ImageStream": tag := image.DefaultImageTag in := ImageStreamTag(g, defaultNamespace(from.Namespace, config.Namespace), from.Name, tag) g.AddEdge(in, node, BuildInputImageGraphEdgeKind) case "ImageStreamTag": name, tag, _ := image.SplitImageStreamTag(from.Name) in := ImageStreamTag(g, defaultNamespace(from.Namespace, config.Namespace), name, tag) g.AddEdge(in, node, BuildInputImageGraphEdgeKind) case "ImageStreamImage": glog.V(4).Infof("Ignoring ImageStreamImage reference in BuildConfig %s/%s", config.Namespace, config.Name) } } return node }
func convert_api_DeploymentCauseImageTrigger_To_v1beta3_DeploymentCauseImageTrigger(in *newer.DeploymentCauseImageTrigger, out *DeploymentCauseImageTrigger, s conversion.Scope) error { if len(in.RepositoryName) != 0 { ref, err := imageapi.ParseDockerImageReference(in.RepositoryName) if err != nil { return err } ref.Tag = in.Tag out.From.Kind = "DockerImage" out.From.Name = ref.String() } return nil }
func convert_v1beta3_DeploymentCauseImageTrigger_To_api_DeploymentCauseImageTrigger(in *DeploymentCauseImageTrigger, out *newer.DeploymentCauseImageTrigger, s conversion.Scope) error { switch in.From.Kind { case "DockerImage": ref, err := imageapi.ParseDockerImageReference(in.From.Name) if err != nil { return err } out.Tag = ref.Tag ref.Tag, ref.ID = "", "" out.RepositoryName = ref.Minimal().String() } return nil }
// ValidateImage tests required fields for an Image. func ValidateImage(image *api.Image) fielderrors.ValidationErrorList { result := fielderrors.ValidationErrorList{} result = append(result, validation.ValidateObjectMeta(&image.ObjectMeta, false, oapi.MinimalNameRequirements).Prefix("metadata")...) if len(image.DockerImageReference) == 0 { result = append(result, fielderrors.NewFieldRequired("dockerImageReference")) } else { if _, err := api.ParseDockerImageReference(image.DockerImageReference); err != nil { result = append(result, fielderrors.NewFieldInvalid("dockerImageReference", image.DockerImageReference, err.Error())) } } return result }
func EachTemplateImage(pod *kapi.PodSpec, triggerFn TriggeredByFunc, fn func(TemplateImage, error)) { for _, container := range pod.Containers { var ref image.DockerImageReference if trigger, ok := triggerFn(&container); ok { trigger.Image = container.Image fn(trigger, nil) continue } ref, err := image.ParseDockerImageReference(container.Image) if err != nil { fn(TemplateImage{Image: container.Image}, err) continue } fn(TemplateImage{Image: container.Image, Ref: &ref}, nil) } }
// Resolve searches the docker registry for repositories matching the passed in value func (r DockerRegistryResolver) Resolve(value string) (*ComponentMatch, error) { ref, err := imageapi.ParseDockerImageReference(value) if err != nil { return nil, err } glog.V(4).Infof("checking Docker registry for %q", ref.String()) connection, err := r.Client.Connect(ref.Registry, r.AllowInsecure) if err != nil { if dockerregistry.IsRegistryNotFound(err) { return nil, ErrNoMatch{value: value} } return nil, ErrNoMatch{value: value, qualifier: fmt.Sprintf("can't connect to %q: %v", ref.Registry, err)} } image, err := connection.ImageByTag(ref.Namespace, ref.Name, ref.Tag) if err != nil { if dockerregistry.IsNotFound(err) { return nil, ErrNoMatch{value: value, qualifier: err.Error()} } return nil, ErrNoMatch{value: value, qualifier: fmt.Sprintf("can't connect to %q: %v", ref.Registry, err)} } if len(ref.Tag) == 0 { ref.Tag = imageapi.DefaultImageTag } glog.V(4).Infof("found image: %#v", image) dockerImage := &imageapi.DockerImage{} if err = kapi.Scheme.Convert(image, dockerImage); err != nil { return nil, err } if len(ref.Registry) == 0 { ref.Registry = "Docker Hub" } return &ComponentMatch{ Value: value, Argument: fmt.Sprintf("--docker-image=%q", value), Name: value, Description: descriptionFor(dockerImage, value, ref.Registry), Builder: IsBuilderImage(dockerImage), Score: 0, Image: dockerImage, ImageTag: ref.Tag, }, nil }
func matchTag(image docker.APIImages, value, registry, namespace, name, tag string) []*ComponentMatch { if len(tag) == 0 { tag = imageapi.DefaultImageTag } matches := []*ComponentMatch{} for _, s := range image.RepoTags { if value == s { matches = append(matches, &ComponentMatch{ Value: s, Score: 0.0, }) continue } iRef, err := imageapi.ParseDockerImageReference(s) if err != nil { continue } if len(iRef.Tag) == 0 { iRef.Tag = imageapi.DefaultImageTag } match := &ComponentMatch{} ok, score := partialScorer(name, iRef.Name, true, 0.5, 1.0) if !ok { continue } match.Score += score _, score = partialScorer(namespace, iRef.Namespace, false, 0.5, 1.0) match.Score += score _, score = partialScorer(registry, iRef.Registry, false, 0.5, 1.0) match.Score += score _, score = partialScorer(tag, iRef.Tag, false, 0.5, 1.0) match.Score += score if match.Score >= 4.0 { continue } match.Score = match.Score / 4.0 glog.V(4).Infof("partial match on %q with %f", s, match.Score) match.Value = s matches = append(matches, match) } return matches }
// ValidateImageStream tests required fields for an ImageStream. func ValidateImageStream(stream *api.ImageStream) fielderrors.ValidationErrorList { result := fielderrors.ValidationErrorList{} result = append(result, validation.ValidateObjectMeta(&stream.ObjectMeta, true, ValidateImageStreamName).Prefix("metadata")...) // Ensure we can generate a valid docker image repository from namespace/name if len(stream.Namespace) > 0 && len(stream.Namespace) < v2.RepositoryNameComponentMinLength { result = append(result, fielderrors.NewFieldInvalid("metadata.namespace", stream.Namespace, fmt.Sprintf("must be at least %d characters long", v2.RepositoryNameComponentMinLength))) } if len(stream.Namespace+"/"+stream.Name) > v2.RepositoryNameTotalLengthMax { result = append(result, fielderrors.NewFieldInvalid("metadata.name", stream.Name, fmt.Sprintf("'namespace/name' cannot be longer than %d characters", v2.RepositoryNameTotalLengthMax))) } if stream.Spec.Tags == nil { stream.Spec.Tags = make(map[string]api.TagReference) } if len(stream.Spec.DockerImageRepository) != 0 { if _, err := api.ParseDockerImageReference(stream.Spec.DockerImageRepository); err != nil { result = append(result, fielderrors.NewFieldInvalid("spec.dockerImageRepository", stream.Spec.DockerImageRepository, err.Error())) } } for tag, tagRef := range stream.Spec.Tags { if tagRef.From != nil { switch tagRef.From.Kind { case "DockerImage", "ImageStreamImage", "ImageStreamTag": default: result = append(result, fielderrors.NewFieldInvalid(fmt.Sprintf("spec.tags[%s].from.kind", tag), tagRef.From.Kind, "valid values are 'DockerImage', 'ImageStreamImage', 'ImageStreamTag'")) } } } for tag, history := range stream.Status.Tags { for i, tagEvent := range history.Items { if len(tagEvent.DockerImageReference) == 0 { result = append(result, fielderrors.NewFieldRequired(fmt.Sprintf("status.tags[%s].Items[%d].dockerImageReference", tag, i))) } } } return result }
// FromStream generates an ImageRef from an OpenShift ImageStream func (g *imageRefGenerator) FromStream(stream *imageapi.ImageStream, tag string) (*ImageRef, error) { pullSpec := stream.Status.DockerImageRepository if len(pullSpec) == 0 { // need to know the default OpenShift registry return nil, fmt.Errorf("the repository does not resolve to a pullable Docker repository") } ref, err := imageapi.ParseDockerImageReference(pullSpec) if err != nil { return nil, err } switch { case len(tag) > 0: ref.Tag = tag case len(tag) == 0 && len(ref.Tag) == 0: ref.Tag = imageapi.DefaultImageTag } return &ImageRef{ DockerImageReference: ref, Stream: stream, }, nil }
func (e *defaultExporter) Export(obj runtime.Object, exact bool) error { if meta, err := kapi.ObjectMetaFor(obj); err == nil { exportObjectMeta(meta, exact) } else { glog.V(4).Infof("Object of type %v does not have ObjectMeta: %v", reflect.TypeOf(obj), err) } switch t := obj.(type) { case *kapi.Endpoints: endpoint.Strategy.PrepareForCreate(obj) case *kapi.ResourceQuota: resourcequota.Strategy.PrepareForCreate(obj) case *kapi.LimitRange: // TODO: this needs to be fixed // limitrange.Strategy.PrepareForCreate(obj) case *kapi.Node: minion.Strategy.PrepareForCreate(obj) if exact { return nil } // Nodes are the only resources that allow direct status edits, therefore // we clear that without exact so that the node value can be reused. t.Status = kapi.NodeStatus{} case *kapi.Namespace: namespace.Strategy.PrepareForCreate(obj) case *kapi.PersistentVolumeClaim: persistentvolumeclaim.Strategy.PrepareForCreate(obj) case *kapi.PersistentVolume: persistentvolume.Strategy.PrepareForCreate(obj) case *kapi.ReplicationController: controller.Strategy.PrepareForCreate(obj) case *kapi.Pod: pod.Strategy.PrepareForCreate(obj) case *kapi.PodTemplate: case *kapi.Service: // TODO: service does not yet have a strategy t.Status = kapi.ServiceStatus{} if exact { return nil } if t.Spec.PortalIP != kapi.PortalIPNone { t.Spec.PortalIP = "" } if t.Spec.Type == kapi.ServiceTypeNodePort { for i := range t.Spec.Ports { t.Spec.Ports[i].NodePort = 0 } } case *kapi.Secret: secret.Strategy.PrepareForCreate(obj) if exact { return nil } // secrets that are tied to the UID of a service account cannot be exported anyway if t.Type == kapi.SecretTypeServiceAccountToken || len(t.Annotations[kapi.ServiceAccountUIDKey]) > 0 { return ErrExportOmit } case *kapi.ServiceAccount: serviceaccount.Strategy.PrepareForCreate(obj) case *deployapi.DeploymentConfig: // TODO: when internal refactor is completed use status reset t.LatestVersion = 0 t.Details = nil for i := range t.Triggers { if p := t.Triggers[i].ImageChangeParams; p != nil { p.LastTriggeredImage = "" } } case *buildapi.BuildConfig: buildconfigrest.Strategy.PrepareForCreate(obj) // TODO: should be handled by prepare for create t.LastVersion = 0 for i := range t.Triggers { if p := t.Triggers[i].ImageChange; p != nil { p.LastTriggeredImageID = "" } } case *buildapi.Build: buildrest.Strategy.PrepareForCreate(obj) // TODO: should be handled by prepare for create t.Duration = 0 t.Message = "" t.Status = buildapi.BuildStatusNew t.StartTimestamp = nil t.CompletionTimestamp = nil if exact { return nil } if t.Config != nil { t.Config = &kapi.ObjectReference{Name: t.Config.Name} } case *routeapi.Route: case *imageapi.Image: case *imageapi.ImageStream: if exact { return nil } // if we point to a docker image repository upstream, copy only the spec tags if len(t.Spec.DockerImageRepository) > 0 { t.Status = imageapi.ImageStreamStatus{} break } // create an image stream that mirrors (each spec tag points to the remote image stream) if len(t.Status.DockerImageRepository) > 0 { ref, err := imageapi.ParseDockerImageReference(t.Status.DockerImageRepository) if err != nil { return err } newSpec := imageapi.ImageStreamSpec{} for name, tag := range t.Status.Tags { if len(tag.Items) > 0 { // copy annotations existing := t.Spec.Tags[name] // point directly to that registry ref.Tag = name existing.From = &kapi.ObjectReference{ Kind: "DockerImage", Name: ref.String(), } newSpec.Tags[name] = existing } } for name, ref := range t.Spec.Tags { if _, ok := t.Status.Tags[name]; ok { continue } // TODO: potentially trim some of these newSpec.Tags[name] = ref } t.Spec = newSpec t.Status = imageapi.ImageStreamStatus{} break } // otherwise, try to snapshot the most recent image as spec items newSpec := imageapi.ImageStreamSpec{} for name, tag := range t.Status.Tags { if len(tag.Items) > 0 { // copy annotations existing := t.Spec.Tags[name] existing.From = &kapi.ObjectReference{ Kind: "DockerImage", Name: tag.Items[0].DockerImageReference, } newSpec.Tags[name] = existing } } t.Spec = newSpec t.Status = imageapi.ImageStreamStatus{} case *imageapi.ImageStreamTag: exportObjectMeta(&t.Image.ObjectMeta, exact) case *imageapi.ImageStreamImage: exportObjectMeta(&t.Image.ObjectMeta, exact) default: glog.V(4).Infof("No export strategy defined for objects of type %v", reflect.TypeOf(obj)) } return nil }
// findStreamDeps accepts an image stream and a list of build // configurations and returns the dependency tree of the specified // image stream func findStreamDeps(stream, tag string, buildConfigList []buildapi.BuildConfig) (*Node, error) { root := &Node{ FullName: stream, Tags: []string{tag}, } namespace, name, err := split(stream) if err != nil { return nil, err } // Search all build configurations in order to find the image // streams depending on the specified image stream var childNamespace, childName, childTag string for _, cfg := range buildConfigList { for _, tr := range cfg.Triggers { from := buildutil.GetImageStreamForStrategy(cfg.Parameters.Strategy) if from == nil || from.Kind != "ImageStreamTag" || tr.ImageChange == nil { continue } // Setup zeroed fields to their default values if from.Namespace == "" { from.Namespace = cfg.Namespace } fromTag := strings.Split(from.Name, ":")[1] parentStream := namespace + "/" + name + ":" + tag if buildutil.NameFromImageStream("", from, fromTag) == parentStream { // Either To & Tag or DockerImageReference will be used as output if cfg.Parameters.Output.To != nil && cfg.Parameters.Output.To.Name != "" { childName = cfg.Parameters.Output.To.Name childTag = cfg.Parameters.Output.Tag if cfg.Parameters.Output.To.Namespace != "" { childNamespace = cfg.Parameters.Output.To.Namespace } else { childNamespace = cfg.Namespace } } else { ref, err := imageapi.ParseDockerImageReference(cfg.Parameters.Output.DockerImageReference) if err != nil { return nil, err } childName = ref.Name childTag = ref.Tag childNamespace = cfg.Namespace } childStream := join(childNamespace, childName) // Build all children and their dependency trees recursively child, err := findStreamDeps(childStream, childTag, buildConfigList) if err != nil { return nil, err } // Add edge between root and child cfgFullName := join(cfg.Namespace, cfg.Name) root.Edges = append(root.Edges, NewEdge(cfgFullName, child.FullName)) // If the child depends on root via more than one tag, we have to make sure // that only one single instance of the child will make it into root.Children cont := false for _, stream := range root.Children { if stream.FullName == child.FullName { // Just pass the tag along and discard the current child stream.Tags = append(stream.Tags, child.Tags...) cont = true break } } if cont { // Do not append this child in root.Children. It's already in there continue } root.Children = append(root.Children, child) } } } return root, nil }
func TestInputImageFromMatch(t *testing.T) { tests := []struct { name string match *ComponentMatch expectedTag string expectedRef string }{ { name: "image stream", match: &ComponentMatch{ ImageStream: &imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Name: "testimage", Namespace: "myns", }, Status: imageapi.ImageStreamStatus{ DockerImageRepository: "test/imagename", }, }, }, expectedRef: "test/imagename:latest", }, { name: "image stream with tag", match: &ComponentMatch{ ImageStream: &imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Name: "testimage", Namespace: "myns", }, Status: imageapi.ImageStreamStatus{ DockerImageRepository: "test/imagename", }, }, ImageTag: "v2", }, expectedRef: "test/imagename:v2", }, { name: "docker image", match: &ComponentMatch{ Image: &imageapi.DockerImage{}, Value: "test/dockerimage", }, expectedRef: "test/dockerimage", }, { name: "docker image with tag", match: &ComponentMatch{ Image: &imageapi.DockerImage{}, Value: "test/dockerimage:tag", }, expectedRef: "test/dockerimage:tag", }, } for _, test := range tests { imgRef, err := InputImageFromMatch(test.match) if err != nil { t.Errorf("%s: unexpected error: %v\n", test.name, err) continue } expectedRef, _ := imageapi.ParseDockerImageReference(test.expectedRef) if !reflect.DeepEqual(imgRef.DockerImageReference, expectedRef) { t.Errorf("%s: unexpected resulting reference: %#v", test.name, imgRef.DockerImageReference) } } }
// Next processes the given image stream, looking for streams that have DockerImageRepository // set but have not yet been marked as "ready". If transient errors occur, err is returned but // the image stream is not modified (so it will be tried again later). If a permanent // failure occurs the image is marked with an annotation. The tags of the original spec image // are left as is (those are updated through status). func (c *ImportController) Next(stream *api.ImageStream) error { if !needsImport(stream) { return nil } name := stream.Spec.DockerImageRepository ref, err := api.ParseDockerImageReference(name) if err != nil { err = fmt.Errorf("invalid docker image repository, cannot import data: %v", err) util.HandleError(err) return c.done(stream, err.Error(), retryCount) } insecure := stream.Annotations != nil && stream.Annotations[api.InsecureRepositoryAnnotation] == "true" conn, err := c.client.Connect(ref.Registry, insecure) if err != nil { return err } tags, err := conn.ImageTags(ref.Namespace, ref.Name) switch { case dockerregistry.IsRepositoryNotFound(err), dockerregistry.IsRegistryNotFound(err): return c.done(stream, err.Error(), retryCount) case err != nil: return err } imageToTag := make(map[string][]string) for tag, image := range tags { if specTag, ok := stream.Spec.Tags[tag]; ok && specTag.From != nil { // spec tag is set to track another tag - do not import continue } imageToTag[image] = append(imageToTag[image], tag) } // no tags to import if len(imageToTag) == 0 { return c.done(stream, "", retryCount) } for id, tags := range imageToTag { dockerImage, err := conn.ImageByID(ref.Namespace, ref.Name, id) switch { case dockerregistry.IsRepositoryNotFound(err), dockerregistry.IsRegistryNotFound(err): return c.done(stream, err.Error(), retryCount) case dockerregistry.IsImageNotFound(err): continue case err != nil: return err } var image api.DockerImage if err := kapi.Scheme.Convert(dockerImage, &image); err != nil { err = fmt.Errorf("could not convert image: %#v", err) util.HandleError(err) return c.done(stream, err.Error(), retryCount) } idTagPresent := false if len(tags) > 1 && hasTag(tags, id) { // only set to true if we have at least 1 tag that isn't the image id idTagPresent = true } for _, tag := range tags { if idTagPresent && id == tag { continue } pullRefTag := tag if idTagPresent { // if there is a tag for the image by its id (tag=tag), we can pull by id pullRefTag = id } pullRef := api.DockerImageReference{ Registry: ref.Registry, Namespace: ref.Namespace, Name: ref.Name, Tag: pullRefTag, } mapping := &api.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{ Name: stream.Name, Namespace: stream.Namespace, }, Tag: tag, Image: api.Image{ ObjectMeta: kapi.ObjectMeta{ Name: id, }, DockerImageReference: pullRef.String(), DockerImageMetadata: image, }, } if err := c.mappings.ImageStreamMappings(stream.Namespace).Create(mapping); err != nil { if errors.IsNotFound(err) { return c.done(stream, err.Error(), retryCount) } return err } } } // we've completed our updates return c.done(stream, "", retryCount) }
func formatImageStreamTags(out *tabwriter.Writer, stream *imageapi.ImageStream) { if len(stream.Status.Tags) == 0 { fmt.Fprintf(out, "Tags:\t<none>\n") return } fmt.Fprint(out, "\nTag\tSpec\tCreated\tPullSpec\tImage\n") sortedTags := []string{} for k := range stream.Status.Tags { sortedTags = append(sortedTags, k) } for k := range stream.Spec.Tags { if _, ok := stream.Status.Tags[k]; !ok { sortedTags = append(sortedTags, k) } } sort.Strings(sortedTags) for _, tag := range sortedTags { tagRef, ok := stream.Spec.Tags[tag] specTag := "" if ok { if tagRef.From != nil { namePair := "" if len(tagRef.From.Namespace) > 0 && tagRef.From.Namespace != stream.Namespace { namePair = fmt.Sprintf("%s/%s", tagRef.From.Namespace, tagRef.From.Name) } else { namePair = tagRef.From.Name } switch tagRef.From.Kind { case "ImageStreamTag", "ImageStreamImage": specTag = namePair case "DockerImage": specTag = tagRef.From.Name default: specTag = fmt.Sprintf("<unknown %s> %s", tagRef.From.Kind, namePair) } } } else { specTag = "<pushed>" } if taglist, ok := stream.Status.Tags[tag]; ok { for _, event := range taglist.Items { d := timeNowFn().Sub(event.Created.Time) image := event.Image ref, err := imageapi.ParseDockerImageReference(event.DockerImageReference) if err == nil { if ref.ID == image { image = "" } } fmt.Fprintf(out, "%s\t%s\t%s ago\t%s\t%v\n", tag, specTag, units.HumanDuration(d), event.DockerImageReference, image) if tag != "" { tag = "" } if specTag != "" { specTag = "" } } } else { fmt.Fprintf(out, "%s\t%s\t\t<not available>\t<not available>\n", tag, specTag) } } }
// Resolve searches all images in local docker server for an image that matches the passed in value func (r DockerClientResolver) Resolve(value string) (*ComponentMatch, error) { ref, err := imageapi.ParseDockerImageReference(value) if err != nil { return nil, err } glog.V(4).Infof("checking local Docker daemon for %q", ref.String()) images, err := r.Client.ListImages(docker.ListImagesOptions{}) if err != nil { return nil, err } matches := ScoredComponentMatches{} for _, image := range images { if tags := matchTag(image, value, ref.Registry, ref.Namespace, ref.Name, ref.Tag); len(tags) > 0 { matches = append(matches, tags...) } } sort.Sort(matches) if exact := matches.Exact(); len(exact) > 0 { matches = exact } else { if r.RegistryResolver != nil { match, err := r.RegistryResolver.Resolve(value) switch err.(type) { case nil: return match, nil case ErrNoMatch: // show our partial matches case ErrMultipleMatches: return nil, err default: return nil, err } } } errs := []error{} for i, match := range matches { if match.Image != nil { continue } updated, err := r.lookup(match.Value) if err != nil { errs = append(errs, err) continue } updated.Score = match.Score updated.ImageTag = ref.Tag updated.Insecure = r.Insecure matches[i] = updated } if len(errs) != 0 { if len(errs) == 1 { err := errs[0] if err == docker.ErrNoSuchImage { return nil, ErrNoMatch{value: value} } return nil, err } return nil, utilerrors.NewAggregate(errs) } switch len(matches) { case 0: return nil, ErrNoMatch{value: value} case 1: return matches[0], nil default: return nil, ErrMultipleMatches{Image: value, Matches: matches} } }
func fuzzInternalObject(t *testing.T, forVersion string, item runtime.Object, seed int64) runtime.Object { f := apitesting.FuzzerFor(t, forVersion, rand.NewSource(seed)) f.Funcs( // Roles and RoleBindings maps are never nil func(j *authorizationapi.Policy, c fuzz.Continue) { j.Roles = make(map[string]*authorizationapi.Role) }, func(j *authorizationapi.PolicyBinding, c fuzz.Continue) { j.RoleBindings = make(map[string]*authorizationapi.RoleBinding) }, func(j *authorizationapi.ClusterPolicy, c fuzz.Continue) { j.Roles = make(map[string]*authorizationapi.ClusterRole) }, func(j *authorizationapi.ClusterPolicyBinding, c fuzz.Continue) { j.RoleBindings = make(map[string]*authorizationapi.ClusterRoleBinding) }, func(j *template.Template, c fuzz.Continue) { c.Fuzz(&j.ObjectMeta) c.Fuzz(&j.Parameters) // TODO: replace with structured type definition j.Objects = []runtime.Object{} }, func(j *image.Image, c fuzz.Continue) { c.Fuzz(&j.ObjectMeta) c.Fuzz(&j.DockerImageMetadata) j.DockerImageMetadata.APIVersion = "" j.DockerImageMetadata.Kind = "" j.DockerImageMetadataVersion = []string{"pre012", "1.0"}[c.Rand.Intn(2)] j.DockerImageReference = c.RandString() }, func(j *image.ImageStreamMapping, c fuzz.Continue) { c.FuzzNoCustom(j) j.DockerImageRepository = "" }, func(j *image.ImageStreamImage, c fuzz.Continue) { c.Fuzz(&j.Image) // because we de-embedded Image from ImageStreamImage, in order to round trip // successfully, the ImageStreamImage's ObjectMeta must match the Image's. j.ObjectMeta = j.Image.ObjectMeta }, func(j *image.ImageStreamTag, c fuzz.Continue) { c.Fuzz(&j.Image) // because we de-embedded Image from ImageStreamTag, in order to round trip // successfully, the ImageStreamTag's ObjectMeta must match the Image's. j.ObjectMeta = j.Image.ObjectMeta }, func(j *image.TagReference, c fuzz.Continue) { c.FuzzNoCustom(j) if j.From != nil { specs := []string{"", "ImageStreamTag", "ImageStreamImage"} j.From.Kind = specs[c.Intn(len(specs))] } }, func(j *build.SourceBuildStrategy, c fuzz.Continue) { c.FuzzNoCustom(j) j.From.Kind = "ImageStreamTag" j.From.Name = "image:tag" j.From.APIVersion = "" j.From.ResourceVersion = "" j.From.FieldPath = "" }, func(j *build.CustomBuildStrategy, c fuzz.Continue) { c.FuzzNoCustom(j) j.From.Kind = "ImageStreamTag" j.From.Name = "image:tag" j.From.APIVersion = "" j.From.ResourceVersion = "" j.From.FieldPath = "" }, func(j *build.DockerBuildStrategy, c fuzz.Continue) { c.FuzzNoCustom(j) j.From.Kind = "ImageStreamTag" j.From.Name = "image:tag" j.From.APIVersion = "" j.From.ResourceVersion = "" j.From.FieldPath = "" }, func(j *build.BuildOutput, c fuzz.Continue) { c.FuzzNoCustom(j) specs := []string{"", "a/b", "a/b/c", "a:5000/b/c", "a/b:latest", "a/b@test"} j.DockerImageReference = specs[c.Intn(len(specs))] j.Tag, j.DockerImageReference = "", "" if j.To != nil && (len(j.To.Kind) == 0 || j.To.Kind == "ImageStream") { j.To.Kind = "ImageStream" j.Tag = image.DefaultImageTag } if j.To != nil && strings.Contains(j.To.Name, ":") { j.To.Name = strings.Replace(j.To.Name, ":", "-", -1) } }, func(j *deploy.DeploymentStrategy, c fuzz.Continue) { c.FuzzNoCustom(j) mkintp := func(i int) *int64 { v := int64(i) return &v } switch c.Intn(3) { case 0: // TODO: we should not have to set defaults, instead we should be able // to detect defaults were applied. j.Type = deploy.DeploymentStrategyTypeRolling j.RollingParams = &deploy.RollingDeploymentStrategyParams{ IntervalSeconds: mkintp(1), UpdatePeriodSeconds: mkintp(1), TimeoutSeconds: mkintp(120), } case 1: j.Type = deploy.DeploymentStrategyTypeRecreate j.RollingParams = nil case 2: j.Type = deploy.DeploymentStrategyTypeCustom j.RollingParams = nil } }, func(j *deploy.DeploymentCauseImageTrigger, c fuzz.Continue) { c.FuzzNoCustom(j) specs := []string{"", "a/b", "a/b/c", "a:5000/b/c", "a/b", "a/b"} tags := []string{"", "stuff", "other"} j.RepositoryName = specs[c.Intn(len(specs))] if len(j.RepositoryName) > 0 { j.Tag = tags[c.Intn(len(tags))] } else { j.Tag = "" } }, func(j *deploy.DeploymentTriggerImageChangeParams, c fuzz.Continue) { c.FuzzNoCustom(j) specs := []string{"a/b", "a/b/c", "a:5000/b/c", "a/b:latest", "a/b@test"} j.From.Kind = "DockerImage" j.From.Name = specs[c.Intn(len(specs))] if ref, err := image.ParseDockerImageReference(j.From.Name); err == nil { j.Tag = ref.Tag ref.Tag, ref.ID = "", "" j.RepositoryName = ref.String() } }, func(j *runtime.EmbeddedObject, c fuzz.Continue) { // runtime.EmbeddedObject causes a panic inside of fuzz because runtime.Object isn't handled. }, func(t *time.Time, c fuzz.Continue) { // This is necessary because the standard fuzzed time.Time object is // completely nil, but when JSON unmarshals dates it fills in the // unexported loc field with the time.UTC object, resulting in // reflect.DeepEqual returning false in the round trip tests. We solve it // by using a date that will be identical to the one JSON unmarshals. *t = time.Date(2000, 1, 1, 1, 1, 1, 0, time.UTC) }, func(u64 *uint64, c fuzz.Continue) { // TODO: uint64's are NOT handled right. *u64 = c.RandUint64() >> 8 }, ) f.Fuzz(item) j, err := meta.TypeAccessor(item) if err != nil { t.Fatalf("Unexpected error %v for %#v", err, item) } j.SetKind("") j.SetAPIVersion("") return item }