Beispiel #1
0
// importSuccessful records a successful import into an image stream, setting the spec tag, status tag or conditions, and ensuring
// the image is created in etcd. Images are cached so they are not created multiple times in a row (when multiple tags point to the
// same image), and a failure to persist the image will be summarized before we update the stream. If an image was imported by this
// operation, it *replaces* the imported image (from the remote repository) with the updated image.
func (r *REST) importSuccessful(
	ctx kapi.Context,
	image *api.Image, stream *api.ImageStream, tag string, from string, nextGeneration int64, now unversioned.Time, importPolicy api.TagImportPolicy,
	importedImages map[string]error, updatedImages map[string]*api.Image,
) (*api.Image, bool) {
	Strategy.PrepareImageForCreate(image)

	pullSpec, _ := api.MostAccuratePullSpec(image.DockerImageReference, image.Name, "")
	tagEvent := api.TagEvent{
		Created:              now,
		DockerImageReference: pullSpec,
		Image:                image.Name,
		Generation:           nextGeneration,
	}

	if stream.Spec.Tags == nil {
		stream.Spec.Tags = make(map[string]api.TagReference)
	}

	// ensure the spec and status tag match the imported image
	changed := api.DifferentTagEvent(stream, tag, tagEvent)
	specTag, ok := stream.Spec.Tags[tag]
	if changed || !ok {
		specTag = ensureSpecTag(stream, tag, from, importPolicy, true)
		api.AddTagEventToImageStream(stream, tag, tagEvent)
	}
	// always reset the import policy
	specTag.ImportPolicy = importPolicy
	stream.Spec.Tags[tag] = specTag

	// import or reuse the image, and ensure tag conditions are set
	importErr, alreadyImported := importedImages[image.Name]
	if importErr != nil {
		api.SetTagConditions(stream, tag, newImportFailedCondition(importErr, nextGeneration, now))
	} else {
		api.SetTagConditions(stream, tag)
	}

	// create the image if it does not exist, otherwise cache the updated status from the store for use by other tags
	if alreadyImported {
		if updatedImage, ok := updatedImages[image.Name]; ok {
			return updatedImage, true
		}
		return nil, false
	}

	updated, err := r.images.Create(ctx, image)
	switch {
	case kapierrors.IsAlreadyExists(err):
		if err := api.ImageWithMetadata(image); err != nil {
			glog.V(4).Infof("Unable to update image metadata during image import when image already exists %q: err", image.Name, err)
		}
		updated = image
		fallthrough
	case err == nil:
		updatedImage := updated.(*api.Image)
		updatedImages[image.Name] = updatedImage
		//isi.Status.Repository.Images[i].Image = updatedImage
		importedImages[image.Name] = nil
		return updatedImage, true
	default:
		importedImages[image.Name] = err
	}
	return nil, false
}
Beispiel #2
0
func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) {
	isi, ok := obj.(*api.ImageStreamImport)
	if !ok {
		return nil, kapierrors.NewBadRequest(fmt.Sprintf("obj is not an ImageStreamImport: %#v", obj))
	}

	inputMeta := isi.ObjectMeta

	if err := rest.BeforeCreate(Strategy, ctx, obj); err != nil {
		return nil, err
	}

	namespace, ok := kapi.NamespaceFrom(ctx)
	if !ok {
		return nil, kapierrors.NewBadRequest("a namespace must be specified to import images")
	}

	secrets, err := r.secrets.ImageStreamSecrets(namespace).Secrets(isi.Name, kapi.ListOptions{})
	if err != nil {
		util.HandleError(fmt.Errorf("unable to load secrets for namespace %q: %v", namespace, err))
		secrets = &kapi.SecretList{}
	}

	if r.clientFn != nil {
		if client := r.clientFn(); client != nil {
			ctx = kapi.WithValue(ctx, importer.ContextKeyV1RegistryClient, client)
		}
	}
	credentials := importer.NewCredentialsForSecrets(secrets.Items)
	importCtx := importer.NewContext(r.transport).WithCredentials(credentials)

	imports := r.importFn(importCtx)
	if err := imports.Import(ctx.(gocontext.Context), isi); err != nil {
		return nil, kapierrors.NewInternalError(err)
	}

	// TODO: perform the transformation of the image stream and return it with the ISI if import is false
	//   so that clients can see what the resulting object would look like.
	if !isi.Spec.Import {
		clearManifests(isi)
		return isi, nil
	}

	create := false
	stream, err := r.streams.GetImageStream(ctx, isi.Name)
	if err != nil {
		if !kapierrors.IsNotFound(err) {
			return nil, err
		}
		// consistency check, stream must exist
		if len(inputMeta.ResourceVersion) > 0 || len(inputMeta.UID) > 0 {
			return nil, err
		}
		create = true
		stream = &api.ImageStream{
			ObjectMeta: kapi.ObjectMeta{
				Name:       isi.Name,
				Namespace:  namespace,
				Generation: 0,
			},
		}
	} else {
		if len(inputMeta.ResourceVersion) > 0 && inputMeta.ResourceVersion != stream.ResourceVersion {
			glog.V(4).Infof("DEBUG: mismatch between requested UID %s and located UID %s", inputMeta.UID, stream.UID)
			return nil, kapierrors.NewConflict("imageStream", inputMeta.Name, fmt.Errorf("the image stream was updated from %q to %q", inputMeta.ResourceVersion, stream.ResourceVersion))
		}
		if len(inputMeta.UID) > 0 && inputMeta.UID != stream.UID {
			glog.V(4).Infof("DEBUG: mismatch between requested UID %s and located UID %s", inputMeta.UID, stream.UID)
			return nil, kapierrors.NewNotFound("imageStream", inputMeta.Name)
		}
	}

	if stream.Annotations == nil {
		stream.Annotations = make(map[string]string)
	}
	now := unversioned.Now()
	stream.Annotations[api.DockerImageRepositoryCheckAnnotation] = now.UTC().Format(time.RFC3339)
	gen := stream.Generation + 1
	zero := int64(0)

	importedImages := make(map[string]error)
	updatedImages := make(map[string]*api.Image)
	if spec := isi.Spec.Repository; spec != nil {
		for i, imageStatus := range isi.Status.Repository.Images {
			image := imageStatus.Image
			if image == nil {
				continue
			}

			// update the spec tag
			ref, err := api.ParseDockerImageReference(image.DockerImageReference)
			if err != nil {
				// ???
				continue
			}
			tag := ref.Tag
			if len(imageStatus.Tag) > 0 {
				tag = imageStatus.Tag
			}
			if _, ok := stream.Spec.Tags[tag]; !ok {
				if stream.Spec.Tags == nil {
					stream.Spec.Tags = make(map[string]api.TagReference)
				}
				stream.Spec.Tags[tag] = api.TagReference{
					From: &kapi.ObjectReference{
						Kind: "DockerImage",
						Name: image.DockerImageReference,
					},
					Generation:   &gen,
					ImportPolicy: api.TagImportPolicy{Insecure: spec.ImportPolicy.Insecure},
				}
			}

			// import or reuse the image
			importErr, imported := importedImages[image.Name]
			if importErr != nil {
				api.SetTagConditions(stream, tag, newImportFailedCondition(err, gen, now))
			}

			pullSpec, _ := api.MostAccuratePullSpec(image.DockerImageReference, image.Name, "")
			api.AddTagEventToImageStream(stream, tag, api.TagEvent{
				Created:              now,
				DockerImageReference: pullSpec,
				Image:                image.Name,
				Generation:           gen,
			})

			if imported {
				if updatedImage, ok := updatedImages[image.Name]; ok {
					isi.Status.Repository.Images[i].Image = updatedImage
				}
				continue
			}

			// establish the image into the store
			updated, err := r.images.Create(ctx, image)
			switch {
			case kapierrors.IsAlreadyExists(err):
				if err := api.ImageWithMetadata(image); err != nil {
					glog.V(4).Infof("Unable to update image metadata during image import when image already exists %q: err", image.Name, err)
				}
				updated = image
				fallthrough
			case err == nil:
				updatedImage := updated.(*api.Image)
				updatedImages[image.Name] = updatedImage
				isi.Status.Repository.Images[i].Image = updatedImage
				importedImages[image.Name] = nil
			default:
				importedImages[image.Name] = err
			}
		}
	}

	for i, spec := range isi.Spec.Images {
		if spec.To == nil {
			continue
		}
		tag := spec.To.Name

		if stream.Spec.Tags == nil {
			stream.Spec.Tags = make(map[string]api.TagReference)
		}
		specTag := stream.Spec.Tags[tag]
		from := spec.From
		specTag.From = &from
		specTag.Generation = &zero
		specTag.ImportPolicy.Insecure = spec.ImportPolicy.Insecure
		stream.Spec.Tags[tag] = specTag

		status := isi.Status.Images[i]
		if status.Image == nil || status.Status.Status == unversioned.StatusFailure {
			message := status.Status.Message
			if len(message) == 0 {
				message = "unknown error prevented import"
			}
			api.SetTagConditions(stream, tag, api.TagEventCondition{
				Type:       api.ImportSuccess,
				Status:     kapi.ConditionFalse,
				Message:    message,
				Reason:     string(status.Status.Reason),
				Generation: gen,

				LastTransitionTime: now,
			})
			continue
		}
		image := status.Image

		importErr, imported := importedImages[image.Name]
		if importErr != nil {
			api.SetTagConditions(stream, tag, newImportFailedCondition(err, gen, now))
		}

		pullSpec, _ := api.MostAccuratePullSpec(image.DockerImageReference, image.Name, "")
		api.AddTagEventToImageStream(stream, tag, api.TagEvent{
			Created:              now,
			DockerImageReference: pullSpec,
			Image:                image.Name,
			Generation:           gen,
		})

		if imported {
			continue
		}
		_, err = r.images.Create(ctx, image)
		if kapierrors.IsAlreadyExists(err) {
			err = nil
		}
		importedImages[image.Name] = err
	}

	// TODO: should we allow partial failure?
	for _, err := range importedImages {
		if err != nil {
			return nil, err
		}
	}

	clearManifests(isi)

	if create {
		obj, err = r.internalStreams.Create(ctx, stream)
	} else {
		obj, _, err = r.internalStreams.Update(ctx, stream)
	}
	if err != nil {
		return nil, err
	}
	isi.Status.Import = obj.(*api.ImageStream)
	return isi, nil
}