// objectMetaAndKind retrieves kind and ObjectMeta from a runtime object, or returns an error. func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (*api.ObjectMeta, string, error) { objectMeta, err := api.ObjectMetaFor(obj) if err != nil { return nil, "", errors.NewInternalError(err) } _, kind, err := typer.ObjectVersionAndKind(obj) if err != nil { return nil, "", errors.NewInternalError(err) } return objectMeta, kind, nil }
func (l *lifecycle) Admit(a admission.Attributes) (err error) { // prevent deletion of immortal namespaces if a.GetOperation() == admission.Delete && a.GetKind() == "Namespace" && l.immortalNamespaces.Has(a.GetName()) { return errors.NewForbidden(a.GetKind(), a.GetName(), fmt.Errorf("namespace can never be deleted")) } defaultVersion, kind, err := api.RESTMapper.VersionAndKindForResource(a.GetResource()) if err != nil { return errors.NewInternalError(err) } mapping, err := api.RESTMapper.RESTMapping(kind, defaultVersion) if err != nil { return errors.NewInternalError(err) } if mapping.Scope.Name() != meta.RESTScopeNameNamespace { return nil } namespaceObj, exists, err := l.store.Get(&api.Namespace{ ObjectMeta: api.ObjectMeta{ Name: a.GetNamespace(), Namespace: "", }, }) if err != nil { return errors.NewInternalError(err) } // refuse to operate on non-existent namespaces if !exists { // in case of latency in our caches, make a call direct to storage to verify that it truly exists or not namespaceObj, err = l.client.Namespaces().Get(a.GetNamespace()) if err != nil { if errors.IsNotFound(err) { return err } return errors.NewInternalError(err) } } // ensure that we're not trying to create objects in terminating namespaces if a.GetOperation() == admission.Create { namespace := namespaceObj.(*api.Namespace) if namespace.Status.Phase != api.NamespaceTerminating { return nil } // TODO: This should probably not be a 403 return admission.NewForbidden(a, fmt.Errorf("Unable to create new content in namespace %s because it is being terminated.", a.GetNamespace())) } return nil }
// TODO: add other common fields that require global validation. func validateCommonFields(obj, old runtime.Object) fielderrors.ValidationErrorList { allErrs := fielderrors.ValidationErrorList{} objectMeta, err := api.ObjectMetaFor(obj) if err != nil { return append(allErrs, errors.NewInternalError(err)) } oldObjectMeta, err := api.ObjectMetaFor(old) if err != nil { return append(allErrs, errors.NewInternalError(err)) } allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(objectMeta, oldObjectMeta)...) return allErrs }
// NewNotFound is a utility function to return a well-formatted admission control error response func NewNotFound(a Attributes) error { name, kind, err := extractKindName(a) if err != nil { return apierrors.NewInternalError(err) } return apierrors.NewNotFound(kind, name) }
// NewForbidden is a utility function to return a well-formatted admission control error response func NewForbidden(a Attributes, internalError error) error { // do not double wrap an error of same type if apierrors.IsForbidden(internalError) { return internalError } name, kind, err := extractKindName(a) if err != nil { return apierrors.NewInternalError(errs.NewAggregate([]error{internalError, err})) } return apierrors.NewForbidden(kind, name, internalError) }
func (e *exists) Admit(a admission.Attributes) (err error) { defaultVersion, kind, err := api.RESTMapper.VersionAndKindForResource(a.GetResource()) if err != nil { return errors.NewInternalError(err) } mapping, err := api.RESTMapper.RESTMapping(kind, defaultVersion) if err != nil { return errors.NewInternalError(err) } if mapping.Scope.Name() != meta.RESTScopeNameNamespace { return nil } namespace := &api.Namespace{ ObjectMeta: api.ObjectMeta{ Name: a.GetNamespace(), Namespace: "", }, Status: api.NamespaceStatus{}, } _, exists, err := e.store.Get(namespace) if err != nil { return errors.NewInternalError(err) } if exists { return nil } // in case of latency in our caches, make a call direct to storage to verify that it truly exists or not _, err = e.client.Namespaces().Get(a.GetNamespace()) if err != nil { if errors.IsNotFound(err) { return err } return errors.NewInternalError(err) } return nil }
// UpdateREST registers the REST handlers for this APIGroupVersion to an existing web service // in the restful Container. It will use the prefix (root/version) to find the existing // web service. If a web service does not exist within the container to support the prefix // this method will return an error. func (g *APIGroupVersion) UpdateREST(container *restful.Container) error { installer := g.newInstaller() var ws *restful.WebService = nil for i, s := range container.RegisteredWebServices() { if s.RootPath() == installer.prefix { ws = container.RegisteredWebServices()[i] break } } if ws == nil { return apierrors.NewInternalError(fmt.Errorf("unable to find an existing webservice for prefix %s", installer.prefix)) } apiResources, registrationErrors := installer.Install(ws) // TODO: g.Version only contains "version" now, it will contain "group/version" in the near future. AddSupportedResourcesWebService(ws, g.Version, apiResources) return errors.NewAggregate(registrationErrors) }
func getAttrs(obj runtime.Object) (objLabels labels.Set, objFields fields.Set, err error) { event, ok := obj.(*api.Event) if !ok { return nil, nil, errors.NewInternalError(fmt.Errorf("object is not of type event: %#v", obj)) } l := event.Labels if l == nil { l = labels.Set{} } return l, fields.Set{ "metadata.name": event.Name, "involvedObject.kind": event.InvolvedObject.Kind, "involvedObject.namespace": event.InvolvedObject.Namespace, "involvedObject.name": event.InvolvedObject.Name, "involvedObject.uid": string(event.InvolvedObject.UID), "involvedObject.apiVersion": event.InvolvedObject.APIVersion, "involvedObject.resourceVersion": fmt.Sprintf("%s", event.InvolvedObject.ResourceVersion), "involvedObject.fieldPath": event.InvolvedObject.FieldPath, "reason": event.Reason, "source": event.Source.Component, }, nil }
func (w *watchCache) GetAllEventsSinceThreadUnsafe(resourceVersion uint64) ([]watchCacheEvent, error) { size := w.endIndex - w.startIndex oldest := w.resourceVersion if size > 0 { oldest = w.cache[w.startIndex%w.capacity].resourceVersion } if resourceVersion == 0 { // resourceVersion = 0 means that we don't require any specific starting point // and we would like to start watching from ~now. // However, to keep backward compatibility, we additionally need to return the // current state and only then start watching from that point. // // TODO: In v2 api, we should stop returning the current state - #13969. allItems := w.store.List() result := make([]watchCacheEvent, len(allItems)) for i, item := range allItems { result[i] = watchCacheEvent{Type: watch.Added, Object: item.(runtime.Object)} } return result, nil } if resourceVersion < oldest { return nil, errors.NewInternalError(fmt.Errorf("too old resource version: %d (%d)", resourceVersion, oldest)) } // Binary seatch the smallest index at which resourceVersion is not smaller than // the given one. f := func(i int) bool { return w.cache[(w.startIndex+i)%w.capacity].resourceVersion >= resourceVersion } first := sort.Search(size, f) result := make([]watchCacheEvent, size-first) for i := 0; i < size-first; i++ { result[i] = w.cache[(w.startIndex+first+i)%w.capacity].watchCacheEvent } return result, nil }