// Update scales the DeploymentConfig for the given Scale subresource, returning the updated Scale. func (r *ScaleREST) Update(ctx kapi.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { deploymentConfig, err := r.registry.GetDeploymentConfig(ctx, name) if err != nil { return nil, false, errors.NewNotFound(extensions.Resource("scale"), name) } old := api.ScaleFromConfig(deploymentConfig) obj, err := objInfo.UpdatedObject(ctx, old) if err != nil { return nil, false, err } scale, ok := obj.(*extensions.Scale) if !ok { return nil, false, errors.NewBadRequest(fmt.Sprintf("wrong object passed to Scale update: %v", obj)) } if errs := extvalidation.ValidateScale(scale); len(errs) > 0 { return nil, false, errors.NewInvalid(extensions.Kind("Scale"), scale.Name, errs) } deploymentConfig.Spec.Replicas = scale.Spec.Replicas if err := r.registry.UpdateDeploymentConfig(ctx, deploymentConfig); err != nil { return nil, false, err } return scale, false, nil }
func (r *ScaleREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { rc, err := r.registry.GetController(ctx, name) if err != nil { return nil, false, errors.NewNotFound(autoscaling.Resource("replicationcontrollers/scale"), name) } oldScale := scaleFromRC(rc) obj, err := objInfo.UpdatedObject(ctx, oldScale) if err != nil { return nil, false, err } if obj == nil { return nil, false, errors.NewBadRequest("nil update passed to Scale") } scale, ok := obj.(*autoscaling.Scale) if !ok { return nil, false, errors.NewBadRequest(fmt.Sprintf("wrong object passed to Scale update: %v", obj)) } if errs := validation.ValidateScale(scale); len(errs) > 0 { return nil, false, errors.NewInvalid(autoscaling.Kind("Scale"), scale.Name, errs) } rc.Spec.Replicas = scale.Spec.Replicas rc.ResourceVersion = scale.ResourceVersion rc, err = r.registry.UpdateController(ctx, rc) if err != nil { return nil, false, err } return scaleFromRC(rc), false, nil }
func (s *REST) Update(ctx kapi.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { oldObj, err := s.Get(ctx, name) if err != nil { return nil, false, err } obj, err := objInfo.UpdatedObject(ctx, oldObj) if err != nil { return nil, false, err } project, ok := obj.(*api.Project) if !ok { return nil, false, fmt.Errorf("not a project: %#v", obj) } s.updateStrategy.PrepareForUpdate(obj, oldObj) if errs := s.updateStrategy.ValidateUpdate(ctx, obj, oldObj); len(errs) > 0 { return nil, false, kerrors.NewInvalid(projectapi.Kind("Project"), project.Name, errs) } namespace, err := s.client.Update(projectutil.ConvertProject(project)) if err != nil { return nil, false, err } return projectutil.ConvertNamespace(namespace), false, nil }
func (m *VirtualStorage) updateRoleBinding(ctx kapi.Context, name string, objInfo rest.UpdatedObjectInfo, allowEscalation bool) (*authorizationapi.RoleBinding, bool, error) { old, err := m.Get(ctx, name) if err != nil { return nil, false, err } obj, err := objInfo.UpdatedObject(ctx, old) if err != nil { return nil, false, err } roleBinding, ok := obj.(*authorizationapi.RoleBinding) if !ok { return nil, false, kapierrors.NewBadRequest(fmt.Sprintf("obj is not a role: %#v", obj)) } if err := rest.BeforeUpdate(m.UpdateStrategy, ctx, obj, old); err != nil { return nil, false, err } if err := m.validateReferentialIntegrity(ctx, roleBinding); err != nil { return nil, false, err } if !allowEscalation { if err := m.confirmNoEscalation(ctx, roleBinding); err != nil { return nil, false, err } } policyBinding, err := m.getPolicyBindingForPolicy(ctx, roleBinding.RoleRef.Namespace, allowEscalation) if err != nil { return nil, false, err } previousRoleBinding, exists := policyBinding.RoleBindings[roleBinding.Name] if !exists { return nil, false, kapierrors.NewNotFound(authorizationapi.Resource("rolebinding"), roleBinding.Name) } if previousRoleBinding.RoleRef != roleBinding.RoleRef { return nil, false, errors.New("roleBinding.RoleRef may not be modified") } if kapi.Semantic.DeepEqual(previousRoleBinding, roleBinding) { return roleBinding, false, nil } roleBinding.ResourceVersion = policyBinding.ResourceVersion policyBinding.RoleBindings[roleBinding.Name] = roleBinding policyBinding.LastModified = unversioned.Now() if err := m.BindingRegistry.UpdatePolicyBinding(ctx, policyBinding); err != nil { return nil, false, err } return roleBinding, false, nil }
// Update associates an identity with a user. // Both the identity and user must already exist. // If the identity is associated with another user already, it is disassociated. func (s *REST) Update(ctx kapi.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { obj, err := objInfo.UpdatedObject(ctx, nil) if err != nil { return nil, false, err } mapping, ok := obj.(*api.UserIdentityMapping) if !ok { return nil, false, kerrs.NewBadRequest("invalid type") } Strategy.PrepareForUpdate(mapping, nil) return s.createOrUpdate(ctx, mapping, false) }
func (m *VirtualStorage) updateRole(ctx kapi.Context, name string, objInfo rest.UpdatedObjectInfo, allowEscalation bool) (*authorizationapi.Role, bool, error) { old, err := m.Get(ctx, name) if err != nil { return nil, false, err } obj, err := objInfo.UpdatedObject(ctx, old) if err != nil { return nil, false, err } role, ok := obj.(*authorizationapi.Role) if !ok { return nil, false, kapierrors.NewBadRequest(fmt.Sprintf("obj is not a role: %#v", obj)) } if err := rest.BeforeUpdate(m.UpdateStrategy, ctx, obj, old); err != nil { return nil, false, err } if !allowEscalation { if err := rulevalidation.ConfirmNoEscalation(ctx, authorizationapi.Resource("role"), role.Name, m.RuleResolver, authorizationinterfaces.NewLocalRoleAdapter(role)); err != nil { return nil, false, err } } policy, err := m.PolicyStorage.GetPolicy(ctx, authorizationapi.PolicyName) if err != nil && kapierrors.IsNotFound(err) { return nil, false, kapierrors.NewNotFound(authorizationapi.Resource("role"), role.Name) } if err != nil { return nil, false, err } oldRole, exists := policy.Roles[role.Name] if !exists { return nil, false, kapierrors.NewNotFound(authorizationapi.Resource("role"), role.Name) } // non-mutating change if kapi.Semantic.DeepEqual(oldRole, role) { return role, false, nil } role.ResourceVersion = policy.ResourceVersion policy.Roles[role.Name] = role policy.LastModified = unversioned.Now() if err := m.PolicyStorage.UpdatePolicy(ctx, policy); err != nil { return nil, false, err } return role, false, nil }
func (p *testPatcher) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { currentPod := p.startingPod if p.numUpdates > 0 { currentPod = p.updatePod } p.numUpdates++ obj, err := objInfo.UpdatedObject(ctx, currentPod) if err != nil { return nil, false, err } inPod := obj.(*api.Pod) if inPod.ResourceVersion != p.updatePod.ResourceVersion { return nil, false, apierrors.NewConflict(api.Resource("pods"), inPod.Name, fmt.Errorf("existing %v, new %v", p.updatePod.ResourceVersion, inPod.ResourceVersion)) } return inPod, false, nil }
func (r *ScaleREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { rs, err := r.registry.GetReplicaSet(ctx, name) if err != nil { return nil, false, errors.NewNotFound(extensions.Resource("replicasets/scale"), name) } oldScale, err := scaleFromReplicaSet(rs) if err != nil { return nil, false, err } obj, err := objInfo.UpdatedObject(ctx, oldScale) if err != nil { return nil, false, err } if obj == nil { return nil, false, errors.NewBadRequest(fmt.Sprintf("nil update passed to Scale")) } scale, ok := obj.(*extensions.Scale) if !ok { return nil, false, errors.NewBadRequest(fmt.Sprintf("wrong object passed to Scale update: %v", obj)) } if errs := extvalidation.ValidateScale(scale); len(errs) > 0 { return nil, false, errors.NewInvalid(extensions.Kind("Scale"), scale.Name, errs) } rs.Spec.Replicas = scale.Spec.Replicas rs.ResourceVersion = scale.ResourceVersion rs, err = r.registry.UpdateReplicaSet(ctx, rs) if err != nil { return nil, false, err } newScale, err := scaleFromReplicaSet(rs) if err != nil { return nil, false, errors.NewBadRequest(fmt.Sprintf("%v", err)) } return newScale, false, err }
func (r *ScaleREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { deployment, err := r.registry.GetDeployment(ctx, name) if err != nil { return nil, false, errors.NewNotFound(extensions.Resource("deployments/scale"), name) } oldScale, err := scaleFromDeployment(deployment) if err != nil { return nil, false, err } obj, err := objInfo.UpdatedObject(ctx, oldScale) if err != nil { return nil, false, err } if obj == nil { return nil, false, errors.NewBadRequest(fmt.Sprintf("nil update passed to Scale")) } scale, ok := obj.(*extensions.Scale) if !ok { return nil, false, errors.NewBadRequest(fmt.Sprintf("expected input object type to be Scale, but %T", obj)) } if errs := extvalidation.ValidateScale(scale); len(errs) > 0 { return nil, false, errors.NewInvalid(extensions.Kind("Scale"), name, errs) } deployment.Spec.Replicas = scale.Spec.Replicas deployment.ResourceVersion = scale.ResourceVersion deployment, err = r.registry.UpdateDeployment(ctx, deployment) if err != nil { return nil, false, err } newScale, err := scaleFromDeployment(deployment) if err != nil { return nil, false, errors.NewBadRequest(fmt.Sprintf("%v", err)) } return newScale, false, nil }
// Update performs an atomic update and set of the object. Returns the result of the update // or an error. If the registry allows create-on-update, the create flow will be executed. // A bool is returned along with the object and any errors, to indicate object creation. func (e *Store) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { key, err := e.KeyFunc(ctx, name) if err != nil { return nil, false, err } var ( creatingObj runtime.Object creating = false ) storagePreconditions := &storage.Preconditions{} if preconditions := objInfo.Preconditions(); preconditions != nil { storagePreconditions.UID = preconditions.UID } out := e.NewFunc() // deleteObj is only used in case a deletion is carried out var deleteObj runtime.Object err = e.Storage.GuaranteedUpdate(ctx, key, out, true, storagePreconditions, func(existing runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) { // Given the existing object, get the new object obj, err := objInfo.UpdatedObject(ctx, existing) if err != nil { return nil, nil, err } // If AllowUnconditionalUpdate() is true and the object specified by the user does not have a resource version, // then we populate it with the latest version. // Else, we check that the version specified by the user matches the version of latest storage object. resourceVersion, err := e.Storage.Versioner().ObjectResourceVersion(obj) if err != nil { return nil, nil, err } doUnconditionalUpdate := resourceVersion == 0 && e.UpdateStrategy.AllowUnconditionalUpdate() version, err := e.Storage.Versioner().ObjectResourceVersion(existing) if err != nil { return nil, nil, err } if version == 0 { if !e.UpdateStrategy.AllowCreateOnUpdate() { return nil, nil, kubeerr.NewNotFound(e.QualifiedResource, name) } creating = true creatingObj = obj if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil { return nil, nil, err } ttl, err := e.calculateTTL(obj, 0, false) if err != nil { return nil, nil, err } return obj, &ttl, nil } creating = false creatingObj = nil if doUnconditionalUpdate { // Update the object's resource version to match the latest storage object's resource version. err = e.Storage.Versioner().UpdateObject(obj, res.ResourceVersion) if err != nil { return nil, nil, err } } else { // Check if the object's resource version matches the latest resource version. newVersion, err := e.Storage.Versioner().ObjectResourceVersion(obj) if err != nil { return nil, nil, err } if newVersion == 0 { // TODO: The Invalid error should has a field for Resource. // After that field is added, we should fill the Resource and // leave the Kind field empty. See the discussion in #18526. qualifiedKind := unversioned.GroupKind{Group: e.QualifiedResource.Group, Kind: e.QualifiedResource.Resource} fieldErrList := field.ErrorList{field.Invalid(field.NewPath("metadata").Child("resourceVersion"), newVersion, "must be specified for an update")} return nil, nil, kubeerr.NewInvalid(qualifiedKind, name, fieldErrList) } if newVersion != version { return nil, nil, kubeerr.NewConflict(e.QualifiedResource, name, fmt.Errorf(OptimisticLockErrorMsg)) } } if err := rest.BeforeUpdate(e.UpdateStrategy, ctx, obj, existing); err != nil { return nil, nil, err } delete := e.shouldDelete(ctx, key, obj, existing) if delete { deleteObj = obj return nil, nil, errEmptiedFinalizers } ttl, err := e.calculateTTL(obj, res.TTL, true) if err != nil { return nil, nil, err } if int64(ttl) != res.TTL { return obj, &ttl, nil } return obj, nil, nil }) if err != nil { // delete the object if err == errEmptiedFinalizers { return e.deleteForEmptyFinalizers(ctx, name, key, deleteObj, storagePreconditions) } if creating { err = storeerr.InterpretCreateError(err, e.QualifiedResource, name) err = rest.CheckGeneratedNameError(e.CreateStrategy, err, creatingObj) } else { err = storeerr.InterpretUpdateError(err, e.QualifiedResource, name) } return nil, false, err } if creating { if e.AfterCreate != nil { if err := e.AfterCreate(out); err != nil { return nil, false, err } } } else { if e.AfterUpdate != nil { if err := e.AfterUpdate(out); err != nil { return nil, false, err } } } if e.Decorator != nil { if err := e.Decorator(out); err != nil { return nil, false, err } } return out, creating, nil }
func (m *VirtualStorage) updateRole(ctx kapi.Context, name string, objInfo rest.UpdatedObjectInfo, allowEscalation bool) (*authorizationapi.Role, bool, error) { var updatedRole *authorizationapi.Role var roleConflicted = false // Retry if the policy update hits a conflict if err := kclient.RetryOnConflict(kclient.DefaultRetry, func() error { policy, err := m.PolicyStorage.GetPolicy(ctx, authorizationapi.PolicyName) if kapierrors.IsNotFound(err) { return kapierrors.NewNotFound(m.Resource, name) } if err != nil { return err } oldRole, exists := policy.Roles[name] if !exists { return kapierrors.NewNotFound(m.Resource, name) } obj, err := objInfo.UpdatedObject(ctx, oldRole) if err != nil { return err } role, ok := obj.(*authorizationapi.Role) if !ok { return kapierrors.NewBadRequest(fmt.Sprintf("obj is not a role: %#v", obj)) } if len(role.ResourceVersion) == 0 && m.UpdateStrategy.AllowUnconditionalUpdate() { role.ResourceVersion = oldRole.ResourceVersion } if err := rest.BeforeUpdate(m.UpdateStrategy, ctx, obj, oldRole); err != nil { return err } if !allowEscalation { if err := rulevalidation.ConfirmNoEscalation(ctx, m.Resource, role.Name, m.RuleResolver, m.CachedRuleResolver, authorizationinterfaces.NewLocalRoleAdapter(role)); err != nil { return err } } // conflict detection if role.ResourceVersion != oldRole.ResourceVersion { // mark as a conflict err, but return an untyped error to escape the retry roleConflicted = true return errors.New(registry.OptimisticLockErrorMsg) } // non-mutating change if kapi.Semantic.DeepEqual(oldRole, role) { updatedRole = role return nil } role.ResourceVersion = policy.ResourceVersion policy.Roles[role.Name] = role policy.LastModified = unversioned.Now() if err := m.PolicyStorage.UpdatePolicy(ctx, policy); err != nil { return err } updatedRole = role return nil }); err != nil { if roleConflicted { // construct the typed conflict error return nil, false, kapierrors.NewConflict(authorizationapi.Resource("name"), name, err) } return nil, false, err } return updatedRole, false, nil }
func (rs *REST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { oldService, err := rs.registry.GetService(ctx, name) if err != nil { return nil, false, err } obj, err := objInfo.UpdatedObject(ctx, oldService) if err != nil { return nil, false, err } service := obj.(*api.Service) if !api.ValidNamespace(ctx, &service.ObjectMeta) { return nil, false, errors.NewConflict(api.Resource("services"), service.Namespace, fmt.Errorf("Service.Namespace does not match the provided context")) } // Copy over non-user fields // TODO: make this a merge function if errs := validation.ValidateServiceUpdate(service, oldService); len(errs) > 0 { return nil, false, errors.NewInvalid(api.Kind("Service"), service.Name, errs) } nodePortOp := portallocator.StartOperation(rs.serviceNodePorts) defer nodePortOp.Finish() assignNodePorts := shouldAssignNodePorts(service) oldNodePorts := CollectServiceNodePorts(oldService) newNodePorts := []int{} if assignNodePorts { for i := range service.Spec.Ports { servicePort := &service.Spec.Ports[i] nodePort := int(servicePort.NodePort) if nodePort != 0 { if !contains(oldNodePorts, nodePort) { err := nodePortOp.Allocate(nodePort) if err != nil { el := field.ErrorList{field.Invalid(field.NewPath("spec", "ports").Index(i).Child("nodePort"), nodePort, err.Error())} return nil, false, errors.NewInvalid(api.Kind("Service"), service.Name, el) } } } else { nodePort, err = nodePortOp.AllocateNext() if err != nil { // TODO: what error should be returned here? It's not a // field-level validation failure (the field is valid), and it's // not really an internal error. return nil, false, errors.NewInternalError(fmt.Errorf("failed to allocate a nodePort: %v", err)) } servicePort.NodePort = int32(nodePort) } // Detect duplicate node ports; this should have been caught by validation, so we panic if contains(newNodePorts, nodePort) { panic("duplicate node port") } newNodePorts = append(newNodePorts, nodePort) } } else { // Validate should have validated that nodePort == 0 } // The comparison loops are O(N^2), but we don't expect N to be huge // (there's a hard-limit at 2^16, because they're ports; and even 4 ports would be a lot) for _, oldNodePort := range oldNodePorts { if !contains(newNodePorts, oldNodePort) { continue } nodePortOp.ReleaseDeferred(oldNodePort) } // Remove any LoadBalancerStatus now if Type != LoadBalancer; // although loadbalancer delete is actually asynchronous, we don't need to expose the user to that complexity. if service.Spec.Type != api.ServiceTypeLoadBalancer { service.Status.LoadBalancer = api.LoadBalancerStatus{} } success, err := rs.healthCheckNodePortUpdate(oldService, service) if !success { return nil, false, err } out, err := rs.registry.UpdateService(ctx, service) if err == nil { el := nodePortOp.Commit() if el != nil { // problems should be fixed by an eventual reconciliation / restart glog.Errorf("error(s) committing NodePorts changes: %v", el) } } return out, false, err }
func (m *VirtualStorage) updateRoleBinding(ctx kapi.Context, name string, objInfo rest.UpdatedObjectInfo, allowEscalation bool) (*authorizationapi.RoleBinding, bool, error) { var updatedRoleBinding *authorizationapi.RoleBinding var roleBindingConflicted = false if err := kclient.RetryOnConflict(kclient.DefaultRetry, func() error { // Do an initial fetch old, err := m.Get(ctx, name) if err != nil { return err } oldRoleBinding, exists := old.(*authorizationapi.RoleBinding) if !exists { return kapierrors.NewBadRequest(fmt.Sprintf("old obj is not a role binding: %#v", old)) } // get the updated object, so we know what namespace we're binding against obj, err := objInfo.UpdatedObject(ctx, old) if err != nil { return err } roleBinding, ok := obj.(*authorizationapi.RoleBinding) if !ok { return kapierrors.NewBadRequest(fmt.Sprintf("obj is not a role binding: %#v", obj)) } // now that we know which roleRef we want to go to, fetch the policyBinding we'll actually be updating, and re-get the oldRoleBinding policyBinding, err := m.getPolicyBindingForPolicy(ctx, roleBinding.RoleRef.Namespace, allowEscalation) if err != nil { return err } oldRoleBinding, exists = policyBinding.RoleBindings[roleBinding.Name] if !exists { return kapierrors.NewNotFound(authorizationapi.Resource("rolebinding"), roleBinding.Name) } if len(roleBinding.ResourceVersion) == 0 && m.UpdateStrategy.AllowUnconditionalUpdate() { roleBinding.ResourceVersion = oldRoleBinding.ResourceVersion } if err := rest.BeforeUpdate(m.UpdateStrategy, ctx, obj, oldRoleBinding); err != nil { return err } if !allowEscalation { if err := m.confirmNoEscalation(ctx, roleBinding); err != nil { return err } } // conflict detection if roleBinding.ResourceVersion != oldRoleBinding.ResourceVersion { // mark as a conflict err, but return an untyped error to escape the retry roleBindingConflicted = true return errors.New(registry.OptimisticLockErrorMsg) } // non-mutating change if kapi.Semantic.DeepEqual(oldRoleBinding, roleBinding) { updatedRoleBinding = roleBinding return nil } roleBinding.ResourceVersion = policyBinding.ResourceVersion policyBinding.RoleBindings[roleBinding.Name] = roleBinding policyBinding.LastModified = unversioned.Now() if err := m.BindingRegistry.UpdatePolicyBinding(ctx, policyBinding); err != nil { return err } updatedRoleBinding = roleBinding return nil }); err != nil { if roleBindingConflicted { // construct the typed conflict error return nil, false, kapierrors.NewConflict(authorizationapi.Resource("rolebinding"), name, err) } return nil, false, err } return updatedRoleBinding, false, nil }
func (r *REST) Update(ctx kapi.Context, tagName string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { name, tag, err := nameAndTag(tagName) if err != nil { return nil, false, err } create := false imageStream, err := r.imageStreamRegistry.GetImageStream(ctx, name) if err != nil { if !kapierrors.IsNotFound(err) { return nil, false, err } namespace, ok := kapi.NamespaceFrom(ctx) if !ok { return nil, false, kapierrors.NewBadRequest("namespace is required on ImageStreamTags") } imageStream = &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: namespace, Name: name, }, } kapi.FillObjectMetaSystemFields(ctx, &imageStream.ObjectMeta) create = true } // create the synthetic old istag old, err := newISTag(tag, imageStream, nil, true) if err != nil { return nil, false, err } obj, err := objInfo.UpdatedObject(ctx, old) if err != nil { return nil, false, err } istag, ok := obj.(*api.ImageStreamTag) if !ok { return nil, false, kapierrors.NewBadRequest(fmt.Sprintf("obj is not an ImageStreamTag: %#v", obj)) } // check for conflict switch { case len(istag.ResourceVersion) == 0: // should disallow blind PUT, but this was previously supported istag.ResourceVersion = imageStream.ResourceVersion case len(imageStream.ResourceVersion) == 0: // image stream did not exist, cannot update return nil, false, kapierrors.NewNotFound(api.Resource("imagestreamtags"), tagName) case imageStream.ResourceVersion != istag.ResourceVersion: // conflicting input and output return nil, false, kapierrors.NewConflict(api.Resource("imagestreamtags"), istag.Name, fmt.Errorf("another caller has updated the resource version to %s", imageStream.ResourceVersion)) } if create { if err := rest.BeforeCreate(Strategy, ctx, obj); err != nil { return nil, false, err } } else { if err := rest.BeforeUpdate(Strategy, ctx, obj, old); err != nil { return nil, false, err } } // update the spec tag if imageStream.Spec.Tags == nil { imageStream.Spec.Tags = map[string]api.TagReference{} } tagRef, exists := imageStream.Spec.Tags[tag] // if the caller set tag, override the spec tag if istag.Tag != nil { tagRef = *istag.Tag tagRef.Name = tag } tagRef.Annotations = istag.Annotations imageStream.Spec.Tags[tag] = tagRef // mutate the image stream var newImageStream *api.ImageStream if create { newImageStream, err = r.imageStreamRegistry.CreateImageStream(ctx, imageStream) } else { newImageStream, err = r.imageStreamRegistry.UpdateImageStream(ctx, imageStream) } if err != nil { return nil, false, err } image, err := r.imageFor(ctx, tag, newImageStream) if err != nil { if !kapierrors.IsNotFound(err) { return nil, false, err } } newISTag, err := newISTag(tag, newImageStream, image, true) return newISTag, !exists, err }