func (m *VirtualStorage) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { if err := rest.BeforeCreate(m.CreateStrategy, ctx, obj); err != nil { return nil, err } role := obj.(*authorizationapi.Role) policy, err := m.EnsurePolicy(ctx) if err != nil { return nil, err } if _, exists := policy.Roles[role.Name]; exists { return nil, kapierrors.NewAlreadyExists("Role", role.Name) } role.ResourceVersion = policy.ResourceVersion policy.Roles[role.Name] = role policy.LastModified = util.Now() if err := m.PolicyStorage.UpdatePolicy(ctx, policy); err != nil { return nil, err } return role, nil }
// CreateMasterServiceIfNeeded will create the specified service if it // doesn't already exist. func (c *Controller) CreateMasterServiceIfNeeded(serviceName string, serviceIP net.IP, servicePort int) error { ctx := api.NewDefaultContext() if _, err := c.ServiceRegistry.GetService(ctx, serviceName); err == nil { // The service already exists. return nil } svc := &api.Service{ ObjectMeta: api.ObjectMeta{ Name: serviceName, Namespace: api.NamespaceDefault, Labels: map[string]string{"provider": "kubernetes", "component": "apiserver"}, }, Spec: api.ServiceSpec{ Ports: []api.ServicePort{{Port: servicePort, Protocol: api.ProtocolTCP}}, // maintained by this code, not by the pod selector Selector: nil, ClusterIP: serviceIP.String(), SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeClusterIP, }, } if err := rest.BeforeCreate(rest.Services, ctx, svc); err != nil { return err } _, err := c.ServiceRegistry.CreateService(ctx, svc) if err != nil && errors.IsAlreadyExists(err) { err = nil } return err }
// Create instantiates a new build from an existing build func (s *CloneREST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { if err := rest.BeforeCreate(clone.Strategy, ctx, obj); err != nil { return nil, err } return s.generator.Clone(ctx, obj.(*buildapi.BuildRequest)) }
func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { if err := rest.BeforeCreate(projectrequestregistry.Strategy, ctx, obj); err != nil { return nil, err } projectRequest := obj.(*projectapi.ProjectRequest) if _, err := r.openshiftClient.Projects().Get(projectRequest.Name); err == nil { return nil, kapierror.NewAlreadyExists("project", projectRequest.Name) } projectName := projectRequest.Name projectAdmin := "" if userInfo, exists := kapi.UserFrom(ctx); exists { projectAdmin = userInfo.GetName() } template, err := r.getTemplate() if err != nil { return nil, err } for i := range template.Parameters { switch template.Parameters[i].Name { case ProjectAdminUserParam: template.Parameters[i].Value = projectAdmin case ProjectDescriptionParam: template.Parameters[i].Value = projectRequest.Description case ProjectDisplayNameParam: template.Parameters[i].Value = projectRequest.DisplayName case ProjectNameParam: template.Parameters[i].Value = projectName } } list, err := r.openshiftClient.TemplateConfigs(kapi.NamespaceDefault).Create(template) if err != nil { return nil, err } if err := utilerrors.NewAggregate(runtime.DecodeList(list.Objects, kapi.Scheme)); err != nil { return nil, err } bulk := configcmd.Bulk{ Mapper: latest.RESTMapper, Typer: kapi.Scheme, RESTClientFactory: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { return r.openshiftClient, nil }, } if err := utilerrors.NewAggregate(bulk.Create(&kapi.List{Items: list.Objects}, projectName)); err != nil { return nil, err } return r.openshiftClient.Projects().Get(projectName) }
// Create registers the given ReplicationController with the system, // which eventually leads to the controller manager acting on its behalf. func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) { controller, ok := obj.(*api.ReplicationController) if !ok { return nil, fmt.Errorf("not a replication controller: %#v", obj) } if err := rest.BeforeCreate(rs.strategy, ctx, obj); err != nil { return nil, err } out, err := rs.registry.CreateController(ctx, controller) if err != nil { err = rest.CheckGeneratedNameError(rs.strategy, err, controller) } return out, err }
// Create satisfies the RESTStorage interface. func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) { minion, ok := obj.(*api.Node) if !ok { return nil, fmt.Errorf("not a minion: %#v", obj) } if err := rest.BeforeCreate(rest.Nodes, ctx, obj); err != nil { return nil, err } if err := rs.registry.CreateMinion(ctx, minion); err != nil { err = rest.CheckGeneratedNameError(rest.Nodes, err, minion) return nil, err } return minion, nil }
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) { service := obj.(*api.Service) if err := rest.BeforeCreate(rest.Services, ctx, obj); err != nil { return nil, err } releaseServiceIP := false defer func() { if releaseServiceIP { if api.IsServiceIPSet(service) { rs.portalMgr.Release(net.ParseIP(service.Spec.PortalIP)) } } }() if api.IsServiceIPRequested(service) { // Allocate next available. ip, err := rs.portalMgr.AllocateNext() if err != nil { return nil, err } service.Spec.PortalIP = ip.String() releaseServiceIP = true } else if api.IsServiceIPSet(service) { // Try to respect the requested IP. if err := rs.portalMgr.Allocate(net.ParseIP(service.Spec.PortalIP)); err != nil { el := fielderrors.ValidationErrorList{fielderrors.NewFieldInvalid("spec.portalIP", service.Spec.PortalIP, err.Error())} return nil, errors.NewInvalid("Service", service.Name, el) } releaseServiceIP = true } out, err := rs.registry.CreateService(ctx, service) if err != nil { err = rest.CheckGeneratedNameError(rest.Services, err, service) } if err == nil { releaseServiceIP = false } return out, err }
// Create registers a new image (if it doesn't exist) and updates the specified ImageStream's tags. func (s *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { if err := rest.BeforeCreate(Strategy, ctx, obj); err != nil { return nil, err } mapping := obj.(*api.ImageStreamMapping) stream, err := s.findStreamForMapping(ctx, mapping) if err != nil { return nil, err } image := mapping.Image tag := mapping.Tag if len(tag) == 0 { tag = api.DefaultImageTag } if err := s.imageRegistry.CreateImage(ctx, &image); err != nil && !errors.IsAlreadyExists(err) { return nil, err } next := api.TagEvent{ Created: util.Now(), DockerImageReference: image.DockerImageReference, Image: image.Name, } if !api.AddTagEventToImageStream(stream, tag, next) { // nothing actually changed return &kapi.Status{Status: kapi.StatusSuccess}, nil } api.UpdateTrackingTags(stream, tag, next) if _, err := s.imageStreamRegistry.UpdateImageStreamStatus(ctx, stream); err != nil { return nil, err } return &kapi.Status{Status: kapi.StatusSuccess}, nil }
// Create inserts a new item according to the unique key from the object. func (e *Etcd) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) { trace := util.NewTrace("Create") defer trace.LogIfLong(time.Second) if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil { return nil, err } name, err := e.ObjectNameFunc(obj) if err != nil { return nil, err } key, err := e.KeyFunc(ctx, name) if err != nil { return nil, err } ttl := uint64(0) if e.TTLFunc != nil { ttl, err = e.TTLFunc(obj, false) if err != nil { return nil, err } } trace.Step("About to create object") out := e.NewFunc() if err := e.Helper.CreateObj(key, obj, out, ttl); err != nil { err = etcderr.InterpretCreateError(err, e.EndpointName, name) err = rest.CheckGeneratedNameError(e.CreateStrategy, err, obj) return nil, err } trace.Step("Object created") if e.AfterCreate != nil { if err := e.AfterCreate(out); err != nil { return nil, err } } if e.Decorator != nil { if err := e.Decorator(obj); err != nil { return nil, err } } return out, nil }
// CreateWithName inserts a new item with the provided name // DEPRECATED: use Create instead func (e *Etcd) CreateWithName(ctx api.Context, name string, obj runtime.Object) error { key, err := e.KeyFunc(ctx, name) if err != nil { return err } if e.CreateStrategy != nil { if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil { return err } } ttl, err := e.calculateTTL(obj, 0, false) if err != nil { return err } err = e.Helper.CreateObj(key, obj, nil, ttl) err = etcderr.InterpretCreateError(err, e.EndpointName, name) if err == nil && e.Decorator != nil { err = e.Decorator(obj) } return err }
// Create inserts a new item according to the unique key from the object. func (e *Etcd) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) { if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil { return nil, err } name, err := e.ObjectNameFunc(obj) if err != nil { return nil, err } key, err := e.KeyFunc(ctx, name) if err != nil { return nil, err } ttl := uint64(0) if e.TTLFunc != nil { ttl, err = e.TTLFunc(obj, false) if err != nil { return nil, err } } out := e.NewFunc() if err := e.Helper.CreateObj(key, obj, out, ttl); err != nil { err = etcderr.InterpretCreateError(err, e.EndpointName, name) err = rest.CheckGeneratedNameError(e.CreateStrategy, err, obj) return nil, err } if e.AfterCreate != nil { if err := e.AfterCreate(out); err != nil { return nil, err } } if e.Decorator != nil { if err := e.Decorator(obj); err != nil { return nil, err } } return out, nil }
func (m *VirtualStorage) createRoleBinding(ctx kapi.Context, obj runtime.Object, allowEscalation bool) (*authorizationapi.RoleBinding, error) { if err := rest.BeforeCreate(m.CreateStrategy, ctx, obj); err != nil { return nil, err } roleBinding := obj.(*authorizationapi.RoleBinding) if err := m.validateReferentialIntegrity(ctx, roleBinding); err != nil { return nil, err } if !allowEscalation { if err := m.confirmNoEscalation(ctx, roleBinding); err != nil { return nil, err } } policyBinding, err := m.getPolicyBindingForPolicy(ctx, roleBinding.RoleRef.Namespace, allowEscalation) if err != nil { return nil, err } _, exists := policyBinding.RoleBindings[roleBinding.Name] if exists { return nil, kapierrors.NewAlreadyExists("RoleBinding", roleBinding.Name) } roleBinding.ResourceVersion = policyBinding.ResourceVersion policyBinding.RoleBindings[roleBinding.Name] = roleBinding policyBinding.LastModified = util.Now() if err := m.BindingRegistry.UpdatePolicyBinding(ctx, policyBinding); err != nil { return nil, err } return roleBinding, nil }
func TestBeforeCreate(t *testing.T) { failures := []runtime.Object{ &api.Service{}, &api.Service{ ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: "#$%%invalid", }, }, &api.Service{ ObjectMeta: api.ObjectMeta{ Name: "##&*(&invalid", Namespace: api.NamespaceDefault, }, }, } for _, test := range failures { ctx := api.NewDefaultContext() err := rest.BeforeCreate(rest.Services, ctx, test) if err == nil { t.Errorf("unexpected non-error for %v", test) } } obj := &api.ReplicationController{ ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: api.NamespaceDefault, }, Spec: api.ReplicationControllerSpec{ Selector: map[string]string{"name": "foo"}, Template: &api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ Labels: map[string]string{ "name": "foo", }, }, Spec: api.PodSpec{ Containers: []api.Container{ { Name: "foo", Image: "foo", ImagePullPolicy: api.PullAlways, }, }, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSDefault, }, }, }, Status: api.ReplicationControllerStatus{ Replicas: 3, }, } ctx := api.NewDefaultContext() err := rest.BeforeCreate(Strategy, ctx, obj) if err != nil { t.Errorf("unexpected error: %v", err) } if !reflect.DeepEqual(obj.Status, api.ReplicationControllerStatus{}) { t.Errorf("status was not cleared as expected.") } if obj.Name != "foo" || obj.Namespace != api.NamespaceDefault { t.Errorf("unexpected object metadata: %v", obj.ObjectMeta) } obj.Spec.Replicas = -1 if err := rest.BeforeCreate(Strategy, ctx, obj); err == nil { t.Errorf("unexpected non-error for invalid replication controller.") } }
func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { if err := rest.BeforeCreate(projectrequestregistry.Strategy, ctx, obj); err != nil { return nil, err } projectRequest := obj.(*projectapi.ProjectRequest) if _, err := r.openshiftClient.Projects().Get(projectRequest.Name); err == nil { return nil, kapierror.NewAlreadyExists("project", projectRequest.Name) } projectName := projectRequest.Name projectAdmin := "" if userInfo, exists := kapi.UserFrom(ctx); exists { projectAdmin = userInfo.GetName() } template, err := r.getTemplate() if err != nil { return nil, err } for i := range template.Parameters { switch template.Parameters[i].Name { case ProjectAdminUserParam: template.Parameters[i].Value = projectAdmin case ProjectDescriptionParam: template.Parameters[i].Value = projectRequest.Description case ProjectDisplayNameParam: template.Parameters[i].Value = projectRequest.DisplayName case ProjectNameParam: template.Parameters[i].Value = projectName } } list, err := r.openshiftClient.TemplateConfigs(kapi.NamespaceDefault).Create(template) if err != nil { return nil, err } if err := utilerrors.NewAggregate(runtime.DecodeList(list.Objects, kapi.Scheme)); err != nil { return nil, kapierror.NewInternalError(err) } // one of the items in this list should be the project. We are going to locate it, remove it from the list, create it separately var projectFromTemplate *projectapi.Project objectsToCreate := &kapi.List{} for i := range list.Objects { if templateProject, ok := list.Objects[i].(*projectapi.Project); ok { projectFromTemplate = templateProject if len(list.Objects) > (i + 1) { objectsToCreate.Items = append(objectsToCreate.Items, list.Objects[i+1:]...) } break } objectsToCreate.Items = append(objectsToCreate.Items, list.Objects[i]) } if projectFromTemplate == nil { return nil, kapierror.NewInternalError(fmt.Errorf("the project template (%s/%s) is not correctly configured: must contain a project resource", r.templateNamespace, r.templateName)) } // we split out project creation separately so that in a case of racers for the same project, only one will win and create the rest of their template objects if _, err := r.openshiftClient.Projects().Create(projectFromTemplate); err != nil { return nil, err } bulk := configcmd.Bulk{ Mapper: latest.RESTMapper, Typer: kapi.Scheme, RESTClientFactory: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { return r.openshiftClient, nil }, } if err := utilerrors.NewAggregate(bulk.Create(objectsToCreate, projectName)); err != nil { return nil, kapierror.NewInternalError(err) } return r.openshiftClient.Projects().Get(projectName) }
// 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 *Etcd) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { trace := util.NewTrace("Update " + reflect.TypeOf(obj).String()) defer trace.LogIfLong(time.Second) name, err := e.ObjectNameFunc(obj) if err != nil { return nil, false, err } key, err := e.KeyFunc(ctx, name) if err != nil { return nil, false, 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 etcd object. resourceVersion, err := e.Helper.Versioner.ObjectResourceVersion(obj) if err != nil { return nil, false, err } doUnconditionalUpdate := resourceVersion == 0 && e.UpdateStrategy.AllowUnconditionalUpdate() // TODO: expose TTL creating := false out := e.NewFunc() err = e.Helper.GuaranteedUpdate(key, out, true, func(existing runtime.Object, res tools.ResponseMeta) (runtime.Object, *uint64, error) { version, err := e.Helper.Versioner.ObjectResourceVersion(existing) if err != nil { return nil, nil, err } if version == 0 { if !e.UpdateStrategy.AllowCreateOnUpdate() { return nil, nil, kubeerr.NewNotFound(e.EndpointName, name) } creating = true 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 if doUnconditionalUpdate { // Update the object's resource version to match the latest etcd object's resource version. err = e.Helper.Versioner.UpdateObject(obj, res.Expiration, 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.Helper.Versioner.ObjectResourceVersion(obj) if err != nil { return nil, nil, err } if newVersion != version { return nil, nil, kubeerr.NewConflict(e.EndpointName, name, fmt.Errorf("the object has been modified; please apply your changes to the latest version and try again")) } } if err := rest.BeforeUpdate(e.UpdateStrategy, ctx, obj, existing); err != nil { return nil, nil, err } 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 { if creating { err = etcderr.InterpretCreateError(err, e.EndpointName, name) err = rest.CheckGeneratedNameError(e.CreateStrategy, err, obj) } else { err = etcderr.InterpretUpdateError(err, e.EndpointName, 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(obj); err != nil { return nil, false, err } } return out, creating, nil }
func (s *REST) createOrUpdate(ctx kapi.Context, obj runtime.Object, forceCreate bool) (runtime.Object, bool, error) { mapping := obj.(*api.UserIdentityMapping) identity, identityErr, oldUser, oldUserErr, oldMapping, oldMappingErr := s.getRelatedObjects(ctx, mapping.Name) // Ensure we didn't get any errors other than NotFound errors if !(oldMappingErr == nil || kerrs.IsNotFound(oldMappingErr)) { return nil, false, oldMappingErr } if !(identityErr == nil || kerrs.IsNotFound(identityErr)) { return nil, false, identityErr } if !(oldUserErr == nil || kerrs.IsNotFound(oldUserErr)) { return nil, false, oldUserErr } // If we expect to be creating, fail if the mapping already existed if forceCreate && oldMappingErr == nil { return nil, false, kerrs.NewAlreadyExists("UserIdentityMapping", oldMapping.Name) } // Allow update to create if missing creating := forceCreate || kerrs.IsNotFound(oldMappingErr) if creating { // Pre-create checks with no access to oldMapping if err := rest.BeforeCreate(Strategy, ctx, mapping); err != nil { return nil, false, err } // Ensure resource version is not specified if len(mapping.ResourceVersion) > 0 { return nil, false, kerrs.NewNotFound("UserIdentityMapping", mapping.Name) } } else { // Pre-update checks with access to oldMapping if err := rest.BeforeUpdate(Strategy, ctx, mapping, oldMapping); err != nil { return nil, false, err } // Ensure resource versions match if len(mapping.ResourceVersion) > 0 && mapping.ResourceVersion != oldMapping.ResourceVersion { return nil, false, kerrs.NewConflict("UserIdentityMapping", mapping.Name, fmt.Errorf("the resource was updated to %s", oldMapping.ResourceVersion)) } // If we're "updating" to the user we're already pointing to, we're already done if mapping.User.Name == oldMapping.User.Name { return oldMapping, false, nil } } // Validate identity if kerrs.IsNotFound(identityErr) { errs := fielderrors.ValidationErrorList([]error{ fielderrors.NewFieldInvalid("identity.name", mapping.Identity.Name, "referenced identity does not exist"), }) return nil, false, kerrs.NewInvalid("UserIdentityMapping", mapping.Name, errs) } // Get new user newUser, err := s.userRegistry.GetUser(ctx, mapping.User.Name) if kerrs.IsNotFound(err) { errs := fielderrors.ValidationErrorList([]error{ fielderrors.NewFieldInvalid("user.name", mapping.User.Name, "referenced user does not exist"), }) return nil, false, kerrs.NewInvalid("UserIdentityMapping", mapping.Name, errs) } if err != nil { return nil, false, err } // Update the new user to point at the identity. If this fails, Update is re-entrant if addIdentityToUser(identity, newUser) { if _, err := s.userRegistry.UpdateUser(ctx, newUser); err != nil { return nil, false, err } } // Update the identity to point at the new user. If this fails. Update is re-entrant if setIdentityUser(identity, newUser) { if updatedIdentity, err := s.identityRegistry.UpdateIdentity(ctx, identity); err != nil { return nil, false, err } else { identity = updatedIdentity } } // At this point, the mapping for the identity has been updated to the new user // Everything past this point is cleanup // Update the old user to no longer point at the identity. // If this fails, log the error, but continue, because Update is no longer re-entrant if oldUser != nil && removeIdentityFromUser(identity, oldUser) { if _, err := s.userRegistry.UpdateUser(ctx, oldUser); err != nil { util.HandleError(fmt.Errorf("error removing identity reference %s from user %s: %v", identity.Name, oldUser.Name, err)) } } updatedMapping, err := mappingFor(newUser, identity) return updatedMapping, creating, err }
// 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 *Etcd) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { name, err := e.ObjectNameFunc(obj) if err != nil { return nil, false, err } key, err := e.KeyFunc(ctx, name) if err != nil { return nil, false, err } // TODO: expose TTL creating := false out := e.NewFunc() err = e.Helper.GuaranteedUpdate(key, out, true, func(existing runtime.Object) (runtime.Object, uint64, error) { version, err := e.Helper.Versioner.ObjectResourceVersion(existing) if err != nil { return nil, 0, err } if version == 0 { if !e.UpdateStrategy.AllowCreateOnUpdate() { return nil, 0, kubeerr.NewNotFound(e.EndpointName, name) } creating = true if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil { return nil, 0, err } ttl := uint64(0) if e.TTLFunc != nil { ttl, err = e.TTLFunc(obj, true) if err != nil { return nil, 0, err } } return obj, ttl, nil } creating = false newVersion, err := e.Helper.Versioner.ObjectResourceVersion(obj) if err != nil { return nil, 0, err } if newVersion != version { // TODO: return the most recent version to a client? return nil, 0, kubeerr.NewConflict(e.EndpointName, name, fmt.Errorf("the resource was updated to %d", version)) } if err := rest.BeforeUpdate(e.UpdateStrategy, ctx, obj, existing); err != nil { return nil, 0, err } ttl := uint64(0) if e.TTLFunc != nil { ttl, err = e.TTLFunc(obj, false) if err != nil { return nil, 0, err } } return obj, ttl, nil }) if err != nil { if creating { err = etcderr.InterpretCreateError(err, e.EndpointName, name) err = rest.CheckGeneratedNameError(e.CreateStrategy, err, obj) } else { err = etcderr.InterpretUpdateError(err, e.EndpointName, 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(obj); err != nil { return nil, false, err } } return out, creating, nil }
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) { service := obj.(*api.Service) if err := rest.BeforeCreate(rest.Services, ctx, obj); err != nil { return nil, err } releaseServiceIP := false defer func() { if releaseServiceIP { if api.IsServiceIPSet(service) { rs.serviceIPs.Release(net.ParseIP(service.Spec.ClusterIP)) } } }() nodePortOp := portallocator.StartOperation(rs.serviceNodePorts) defer nodePortOp.Finish() if api.IsServiceIPRequested(service) { // Allocate next available. ip, err := rs.serviceIPs.AllocateNext() if err != nil { el := fielderrors.ValidationErrorList{fielderrors.NewFieldInvalid("spec.clusterIP", service.Spec.ClusterIP, err.Error())} return nil, errors.NewInvalid("Service", service.Name, el) } service.Spec.ClusterIP = ip.String() releaseServiceIP = true } else if api.IsServiceIPSet(service) { // Try to respect the requested IP. if err := rs.serviceIPs.Allocate(net.ParseIP(service.Spec.ClusterIP)); err != nil { el := fielderrors.ValidationErrorList{fielderrors.NewFieldInvalid("spec.clusterIP", service.Spec.ClusterIP, err.Error())} return nil, errors.NewInvalid("Service", service.Name, el) } releaseServiceIP = true } assignNodePorts := shouldAssignNodePorts(service) for i := range service.Spec.Ports { servicePort := &service.Spec.Ports[i] if servicePort.NodePort != 0 { err := nodePortOp.Allocate(servicePort.NodePort) if err != nil { el := fielderrors.ValidationErrorList{fielderrors.NewFieldInvalid("nodePort", servicePort.NodePort, err.Error())}.PrefixIndex(i).Prefix("spec.ports") return nil, errors.NewInvalid("Service", service.Name, el) } } else if assignNodePorts { nodePort, err := nodePortOp.AllocateNext() if err != nil { el := fielderrors.ValidationErrorList{fielderrors.NewFieldInvalid("nodePort", servicePort.NodePort, err.Error())}.PrefixIndex(i).Prefix("spec.ports") return nil, errors.NewInvalid("Service", service.Name, el) } servicePort.NodePort = nodePort } } out, err := rs.registry.CreateService(ctx, service) if err != nil { err = rest.CheckGeneratedNameError(rest.Services, err, service) } if err == nil { el := nodePortOp.Commit() if el != nil { // these should be caught by an eventual reconciliation / restart glog.Errorf("error(s) committing service node-ports changes: %v", el) } releaseServiceIP = false } return out, err }
// 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 *Etcd) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { trace := util.NewTrace("Update " + reflect.TypeOf(obj).String()) defer trace.LogIfLong(time.Second) name, err := e.ObjectNameFunc(obj) if err != nil { return nil, false, err } key, err := e.KeyFunc(ctx, name) if err != nil { return nil, false, err } // TODO: expose TTL creating := false out := e.NewFunc() err = e.Helper.GuaranteedUpdate(key, out, true, func(existing runtime.Object, res tools.ResponseMeta) (runtime.Object, *uint64, error) { version, err := e.Helper.Versioner.ObjectResourceVersion(existing) if err != nil { return nil, nil, err } if version == 0 { if !e.UpdateStrategy.AllowCreateOnUpdate() { return nil, nil, kubeerr.NewNotFound(e.EndpointName, name) } creating = true 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 newVersion, err := e.Helper.Versioner.ObjectResourceVersion(obj) if err != nil { return nil, nil, err } if newVersion != version { // TODO: return the most recent version to a client? return nil, nil, kubeerr.NewConflict(e.EndpointName, name, fmt.Errorf("the object has been modified; please apply your changes to the latest version and try again")) } if err := rest.BeforeUpdate(e.UpdateStrategy, ctx, obj, existing); err != nil { return nil, nil, err } 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 { if creating { err = etcderr.InterpretCreateError(err, e.EndpointName, name) err = rest.CheckGeneratedNameError(e.CreateStrategy, err, obj) } else { err = etcderr.InterpretUpdateError(err, e.EndpointName, 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(obj); err != nil { return nil, false, err } } return out, creating, nil }