// ObjectReaction returns a ReactionFunc that takes a generic action string of the form // <verb>-<resource> or <verb>-<subresource>-<resource> and attempts to return a runtime // Object or error that matches the requested action. For instance, list-replicationControllers // should attempt to return a list of replication controllers. This method delegates to the // ObjectRetriever interface to satisfy retrieval of lists or retrieval of single items. // TODO: add support for sub resources func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc { return func(action Action) (runtime.Object, error) { _, kind, err := mapper.VersionAndKindForResource(action.GetResource()) if err != nil { return nil, fmt.Errorf("unrecognized action %s: %v", action.GetResource(), err) } // TODO: have mapper return a Kind for a subresource? switch castAction := action.(type) { case ListAction: return o.Kind(kind+"List", "") case GetAction: return o.Kind(kind, castAction.GetName()) case DeleteAction: return o.Kind(kind, castAction.GetName()) case CreateAction: meta, err := api.ObjectMetaFor(castAction.GetObject()) if err != nil { return nil, err } return o.Kind(kind, meta.Name) case UpdateAction: meta, err := api.ObjectMetaFor(castAction.GetObject()) if err != nil { return nil, err } return o.Kind(kind, meta.Name) default: return nil, fmt.Errorf("no reaction implemented for %s", action) } return nil, nil } }
func labelFunc(obj runtime.Object, overwrite bool, resourceVersion string, labels map[string]string, remove []string) (runtime.Object, error) { meta, err := api.ObjectMetaFor(obj) if err != nil { return nil, err } if !overwrite { if err := validateNoOverwrites(meta, labels); err != nil { return nil, err } } if meta.Labels == nil { meta.Labels = make(map[string]string) } for key, value := range labels { meta.Labels[key] = value } for _, label := range remove { delete(meta.Labels, label) } if len(resourceVersion) != 0 { meta.ResourceVersion = resourceVersion } return obj, nil }
// annotationFor returns the annotation with key for obj. func annotationFor(obj runtime.Object, key string) string { meta, err := api.ObjectMetaFor(obj) if err != nil { return "" } return meta.Annotations[key] }
func (t *Tester) TestCreateHasMetadata(valid runtime.Object) { objectMeta, err := api.ObjectMetaFor(valid) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid) } objectMeta.Name = "test" objectMeta.Namespace = api.NamespaceDefault context := api.NewDefaultContext() if t.clusterScope { objectMeta.Namespace = api.NamespaceNone context = api.NewContext() } obj, err := t.storage.(rest.Creater).Create(context, valid) if err != nil { t.Fatalf("Unexpected error: %v", err) } if obj == nil { t.Fatalf("Unexpected object from result: %#v", obj) } if !api.HasObjectMetaSystemFieldValues(objectMeta) { t.Errorf("storage did not populate object meta field values") } }
func AddMountedSecretEdges(g osgraph.Graph, podSpec *kubegraph.PodSpecNode) { //pod specs are always contained. We'll get the toplevel container so that we can pull a namespace from it containerNode := osgraph.GetTopLevelContainerNode(g, podSpec) containerObj := g.GraphDescriber.Object(containerNode) meta, err := kapi.ObjectMetaFor(containerObj.(runtime.Object)) if err != nil { // this should never happen. it means that a podSpec is owned by a top level container that is not a runtime.Object panic(err) } for _, volume := range podSpec.Volumes { source := volume.VolumeSource if source.Secret == nil { continue } // pod secrets must be in the same namespace syntheticSecret := &kapi.Secret{} syntheticSecret.Namespace = meta.Namespace syntheticSecret.Name = source.Secret.SecretName secretNode := kubegraph.FindOrCreateSyntheticSecretNode(g, syntheticSecret) g.AddEdge(podSpec, secretNode, MountedSecretEdgeKind) } }
func GetUniqueRuntimeObjectNodeName(nodeKind string, obj runtime.Object) UniqueName { meta, err := kapi.ObjectMetaFor(obj) if err != nil { panic(err) } return UniqueName(fmt.Sprintf("%s|%s/%s", nodeKind, meta.Namespace, meta.Name)) }
// ObjectResourceVersion implements Versioner func (a APIObjectVersioner) ObjectResourceVersion(obj runtime.Object) (uint64, error) { meta, err := api.ObjectMetaFor(obj) if err != nil { return 0, err } version := meta.ResourceVersion if len(version) == 0 { return 0, nil } return strconv.ParseUint(version, 10, 64) }
// 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 objectMetaData(raw interface{}) (runtime.Object, *kapi.ObjectMeta, error) { obj, ok := raw.(runtime.Object) if !ok { return nil, nil, fmt.Errorf("%#v is not a runtime.Object", raw) } meta, err := kapi.ObjectMetaFor(obj) if err != nil { return nil, nil, err } return obj, meta, nil }
// UpdateObject implements EtcdVersioner func (a APIObjectVersioner) UpdateObject(obj runtime.Object, node *etcd.Node) error { objectMeta, err := api.ObjectMetaFor(obj) if err != nil { return err } version := node.ModifiedIndex versionString := "" if version != 0 { versionString = strconv.FormatUint(version, 10) } objectMeta.ResourceVersion = versionString return nil }
func (t *Tester) TestCreateDiscardsObjectNamespace(valid runtime.Object) { objectMeta, err := api.ObjectMetaFor(valid) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid) } // Ignore non-empty namespace in object meta objectMeta.Namespace = "not-default" // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted created, err := t.storage.(rest.Creater).Create(t.TestContext(), copyOrDie(valid)) if err != nil { t.Fatalf("Unexpected error: %v", err) } createdObjectMeta, err := api.ObjectMetaFor(created) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, created) } if createdObjectMeta.Namespace != api.NamespaceNone { t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.Namespace) } }
func (t *Tester) TestDeleteInvokesValidation(invalid ...runtime.Object) { for i, obj := range invalid { objectMeta, err := api.ObjectMetaFor(obj) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, obj) } ctx := api.NewDefaultContext() _, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil) if !errors.IsInvalid(err) { t.Errorf("%d: Expected to get an invalid resource error, got %v", i, err) } } }
func (t *Tester) TestCreateGeneratesNameReturnsServerTimeout(valid runtime.Object) { objectMeta, err := api.ObjectMetaFor(valid) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid) } objectMeta.GenerateName = "test-" t.withStorageError(errors.NewAlreadyExists("kind", "thing"), func() { _, err := t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid) if err == nil || !errors.IsServerTimeout(err) { t.Fatalf("Unexpected error: %v", err) } }) }
func validateObject(path string, obj runtime.Object, t *testing.T) { // if an object requires a namespace server side, be sure that it is filled in for validation if validation.HasObjectMeta(obj) { namespaceRequired, err := validation.GetRequiresNamespace(obj) if err != nil { t.Errorf("Expected no error, Got %v", err) return } if namespaceRequired { objectMeta, err := kapi.ObjectMetaFor(obj) if err != nil { t.Errorf("Expected no error, Got %v", err) return } objectMeta.Namespace = kapi.NamespaceDefault } } switch typedObj := obj.(type) { case *kapi.Pod: if errors := kvalidation.ValidatePod(typedObj); len(errors) > 0 { t.Errorf("%s did not validate correctly: %v", path, errors) } case *kapi.Service: if errors := kvalidation.ValidateService(typedObj); len(errors) > 0 { t.Errorf("%s did not validate correctly: %v", path, errors) } case *kapi.List, *imageapi.ImageStreamList: if list, err := runtime.ExtractList(typedObj); err == nil { runtime.DecodeList(list, kapi.Scheme) for i := range list { validateObject(path, list[i], t) } } else { t.Errorf("Expected no error, Got %v", err) } default: if errors := validation.Validator.Validate(obj); len(errors) > 0 { t.Errorf("%s with %v did not validate correctly: %v", path, reflect.TypeOf(obj), errors) } } }
// mappedAnnotationFor finds the given annotation in obj using the annotation // map to search all known key variants. func mappedAnnotationFor(obj runtime.Object, key string) string { meta, err := api.ObjectMetaFor(obj) if err != nil { return "" } for _, mappedKey := range annotationMap[key] { if val, ok := meta.Annotations[mappedKey]; ok { return val } } if val, ok := meta.Annotations[key]; ok { return val } return "" }
func (t *Tester) TestCreateRejectsNamespace(valid runtime.Object) { objectMeta, err := api.ObjectMetaFor(valid) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid) } objectMeta.Namespace = "not-default" _, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid) if err == nil { t.Errorf("Expected an error, but we didn't get one") } else if strings.Contains(err.Error(), "Controller.Namespace does not match the provided context") { t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err) } }
func (t *Tester) TestCreateRejectsMismatchedNamespace(valid runtime.Object) { objectMeta, err := api.ObjectMetaFor(valid) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid) } objectMeta.Namespace = "not-default" _, err = t.storage.(rest.Creater).Create(t.TestContext(), valid) if err == nil { t.Errorf("Expected an error, but we didn't get one") } else if !strings.Contains(err.Error(), "does not match the namespace sent on the request") { t.Errorf("Expected 'does not match the namespace sent on the request' error, got '%v'", err.Error()) } }
// UpdateObject implements Versioner func (a APIObjectVersioner) UpdateObject(obj runtime.Object, expiration *time.Time, resourceVersion uint64) error { objectMeta, err := api.ObjectMetaFor(obj) if err != nil { return err } if expiration != nil { objectMeta.DeletionTimestamp = &util.Time{*expiration} } versionString := "" if resourceVersion != 0 { versionString = strconv.FormatUint(resourceVersion, 10) } objectMeta.ResourceVersion = versionString return nil }
func (t *Tester) TestDeleteGracefulUsesZeroOnNil(existing runtime.Object, expectedGrace int64) { objectMeta, err := api.ObjectMetaFor(existing) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing) } ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace) _, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil) if err != nil { t.Errorf("unexpected error: %v", err) } if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) { t.Errorf("unexpected error, object should exist: %v", err) } }
func (t *Tester) TestDeleteNonExist(createFn func() runtime.Object) { existing := createFn() objectMeta, err := api.ObjectMetaFor(existing) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing) } context := api.NewDefaultContext() t.withStorageError(&etcd.EtcdError{ErrorCode: tools.EtcdErrorCodeNotFound}, func() { _, err := t.storage.(rest.GracefulDeleter).Delete(context, objectMeta.Name, nil) if err == nil || !errors.IsNotFound(err) { t.Fatalf("Unexpected error: %v", err) } }) }
// UpdateObject implements EtcdVersioner func (a APIObjectVersioner) UpdateObject(obj runtime.Object, node *etcd.Node) error { objectMeta, err := api.ObjectMetaFor(obj) if err != nil { return err } if ttl := node.Expiration; ttl != nil { objectMeta.DeletionTimestamp = &util.Time{*node.Expiration} } version := node.ModifiedIndex versionString := "" if version != 0 { versionString = strconv.FormatUint(version, 10) } objectMeta.ResourceVersion = versionString return nil }
func (t *Tester) TestCreateGeneratesName(valid runtime.Object) { objectMeta, err := api.ObjectMetaFor(valid) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid) } objectMeta.GenerateName = "test-" _, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid) if err != nil { t.Fatalf("Unexpected error: %v", err) } if objectMeta.Name == "test-" || !strings.HasPrefix(objectMeta.Name, "test-") { t.Errorf("unexpected name: %#v", valid) } }
func (t *Tester) TestDeleteNoGraceful(createFn func() runtime.Object, wasGracefulFn func() bool) { existing := createFn() objectMeta, err := api.ObjectMetaFor(existing) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing) } ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace) _, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(10)) if err != nil { t.Errorf("unexpected error: %v", err) } if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) { t.Errorf("unexpected error, object should not exist: %v", err) } if wasGracefulFn() { t.Errorf("resource should not support graceful delete") } }
func (t *Tester) TestDeleteGracefulHasDefault(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) { objectMeta, err := api.ObjectMetaFor(existing) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing) } ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace) _, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{}) if err != nil { t.Errorf("unexpected error: %v", err) } if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); err != nil { t.Errorf("unexpected error, object should exist: %v", err) } if !wasGracefulFn() { t.Errorf("did not gracefully delete resource") } }
func (t *Tester) TestCreateResetsUserData(valid runtime.Object) { objectMeta, err := api.ObjectMetaFor(valid) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid) } now := util.Now() objectMeta.UID = "bad-uid" objectMeta.CreationTimestamp = now obj, err := t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid) if err != nil { t.Fatalf("Unexpected error: %v", err) } if obj == nil { t.Fatalf("Unexpected object from result: %#v", obj) } if objectMeta.UID == "bad-uid" || objectMeta.CreationTimestamp == now { t.Errorf("ObjectMeta did not reset basic fields: %#v", objectMeta) } }
func (t *Tester) TestCreateHasMetadata(valid runtime.Object) { objectMeta, err := api.ObjectMetaFor(valid) if err != nil { t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid) } objectMeta.Name = "" objectMeta.GenerateName = "test-" objectMeta.Namespace = t.TestNamespace() obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid) if err != nil { t.Fatalf("Unexpected error: %v", err) } if obj == nil { t.Fatalf("Unexpected object from result: %#v", obj) } if !api.HasObjectMetaSystemFieldValues(objectMeta) { t.Errorf("storage did not populate object meta field values") } }
func AddRequestedServiceAccountEdges(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) { //pod specs are always contained. We'll get the toplevel container so that we can pull a namespace from it containerNode := osgraph.GetTopLevelContainerNode(g, podSpecNode) containerObj := g.GraphDescriber.Object(containerNode) meta, err := kapi.ObjectMetaFor(containerObj.(runtime.Object)) if err != nil { panic(err) } // if no SA name is present, admission will set 'default' name := "default" if len(podSpecNode.ServiceAccountName) > 0 { name = podSpecNode.ServiceAccountName } syntheticSA := &kapi.ServiceAccount{} syntheticSA.Namespace = meta.Namespace syntheticSA.Name = name saNode := kubegraph.FindOrCreateSyntheticServiceAccountNode(g, syntheticSA) g.AddEdge(podSpecNode, saNode, ReferencedServiceAccountEdgeKind) }
// Change records the given event (setting the object's resource version) and // sends a watch event with the specified probability. func (f *FakeControllerSource) Change(e watch.Event, watchProbability float64) { f.lock.Lock() defer f.lock.Unlock() objMeta, err := api.ObjectMetaFor(e.Object) if err != nil { panic(err) // this is test code only } resourceVersion := len(f.changes) objMeta.ResourceVersion = strconv.Itoa(resourceVersion) f.changes = append(f.changes, e) key := f.key(objMeta) switch e.Type { case watch.Added, watch.Modified: f.items[key] = e.Object case watch.Deleted: delete(f.items, key) } if rand.Float64() < watchProbability { f.broadcaster.Action(e.Type, e.Object) } }
// 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 := "Unknown" kind := a.GetKind() obj := a.GetObject() if obj != nil { objectMeta, err := api.ObjectMetaFor(obj) if err != nil { return apierrors.NewForbidden(kind, name, internalError) } // this is necessary because name object name generation has not occurred yet if len(objectMeta.Name) > 0 { name = objectMeta.Name } else if len(objectMeta.GenerateName) > 0 { name = objectMeta.GenerateName } } return apierrors.NewForbidden(kind, name, internalError) }
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 }