func installCoreAPIs(s *options.ServerRunOptions, g *genericapiserver.GenericAPIServer, restOptionsFactory restOptionsFactory) { serviceStore, serviceStatusStore := serviceetcd.NewREST(restOptionsFactory.NewFor(api.Resource("service"))) namespaceStore, namespaceStatusStore, namespaceFinalizeStore := namespaceetcd.NewREST(restOptionsFactory.NewFor(api.Resource("namespaces"))) secretStore := secretetcd.NewREST(restOptionsFactory.NewFor(api.Resource("secrets"))) configMapStore := configmapetcd.NewREST(restOptionsFactory.NewFor(api.Resource("configmaps"))) eventStore := eventetcd.NewREST(restOptionsFactory.NewFor(api.Resource("events")), uint64(s.EventTTL.Seconds())) coreResources := map[string]rest.Storage{ "secrets": secretStore, "services": serviceStore, "services/status": serviceStatusStore, "namespaces": namespaceStore, "namespaces/status": namespaceStatusStore, "namespaces/finalize": namespaceFinalizeStore, "events": eventStore, "configmaps": configMapStore, } coreGroupMeta := registered.GroupOrDie(core.GroupName) apiGroupInfo := genericapiserver.APIGroupInfo{ GroupMeta: *coreGroupMeta, VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ v1.SchemeGroupVersion.Version: coreResources, }, OptionsExternalVersion: ®istered.GroupOrDie(core.GroupName).GroupVersion, Scheme: core.Scheme, ParameterCodec: core.ParameterCodec, NegotiatedSerializer: core.Codecs, } if err := g.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo); err != nil { glog.Fatalf("Error in registering group version: %+v.\n Error: %v\n", apiGroupInfo, err) } }
// Ensure that when scheduler creates a binding for a pod that has already been deleted // by the API server, API server returns not-found error. func TestEtcdCreateBindingNoPod(t *testing.T) { storage, bindingStorage, _, server := newStorage(t) defer server.Terminate(t) defer storage.Store.DestroyFunc() ctx := api.NewDefaultContext() key, _ := storage.KeyFunc(ctx, "foo") key = etcdtest.AddPrefix(key) // Assume that a pod has undergone the following: // - Create (apiserver) // - Schedule (scheduler) // - Delete (apiserver) _, err := bindingStorage.Create(ctx, &api.Binding{ ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, Target: api.ObjectReference{Name: "machine"}, }) if err == nil { t.Fatalf("Expected not-found-error but got nothing") } if !errors.IsNotFound(storeerr.InterpretGetError(err, api.Resource("pods"), "foo")) { t.Fatalf("Unexpected error returned: %#v", err) } _, err = storage.Get(ctx, "foo") if err == nil { t.Fatalf("Expected not-found-error but got nothing") } if !errors.IsNotFound(storeerr.InterpretGetError(err, api.Resource("pods"), "foo")) { t.Fatalf("Unexpected error: %v", err) } }
// InstallAPI starts a Kubernetes master and registers the supported REST APIs // into the provided mux, then returns an array of strings indicating what // endpoints were started (these are format strings that will expect to be sent // a single string value). func (c *MasterConfig) InstallAPI(container *restful.Container) ([]string, error) { c.Master.RestfulContainer = container if c.Master.EnableCoreControllers { glog.V(2).Info("Using the lease endpoint reconciler") leaseStorage, err := c.Master.StorageFactory.New(kapi.Resource("apiServerIPInfo")) if err != nil { glog.Fatalf(err.Error()) } leaseTTL := uint64(master.DefaultEndpointReconcilerInterval + 5) // add 5 seconds for wiggle room masterLeases := election.NewLeases(leaseStorage, "/masterleases/", leaseTTL) storage, err := c.Master.StorageFactory.New(kapi.Resource("endpoints")) if err != nil { glog.Fatalf(err.Error()) } endpointsStorage := endpointsetcd.NewREST(generic.RESTOptions{ Storage: storage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 0, }) endpointRegistry := endpoint.NewRegistry(endpointsStorage) c.Master.EndpointReconcilerConfig = master.EndpointReconcilerConfig{ Reconciler: election.NewLeaseEndpointReconciler(endpointRegistry, masterLeases), Interval: master.DefaultEndpointReconcilerInterval, } } _, err := master.New(c.Master) if err != nil { return nil, err } messages := []string{} // v1 has to be printed separately since it's served from different endpoint than groups if configapi.HasKubernetesAPIVersion(c.Options, v1.SchemeGroupVersion) { messages = append(messages, fmt.Sprintf("Started Kubernetes API at %%s%s", KubeAPIPrefix)) } versions := []unversioned.GroupVersion{ extv1beta1.SchemeGroupVersion, batchv1.SchemeGroupVersion, autoscalingv1.SchemeGroupVersion, appsv1alpha1.SchemeGroupVersion, } for _, ver := range versions { if configapi.HasKubernetesAPIVersion(c.Options, ver) { messages = append(messages, fmt.Sprintf("Started Kubernetes API %s at %%s%s", ver.String(), KubeAPIGroupPrefix)) } } return messages, nil }
// assignPod assigns the given pod to the given machine. func (r *BindingREST) assignPod(ctx api.Context, podID string, machine string, annotations map[string]string) (err error) { if _, err = r.setPodHostAndAnnotations(ctx, podID, "", machine, annotations); err != nil { err = storeerr.InterpretGetError(err, api.Resource("pods"), podID) err = storeerr.InterpretUpdateError(err, api.Resource("pods"), podID) if _, ok := err.(*errors.StatusError); !ok { err = errors.NewConflict(api.Resource("pods/binding"), podID, err) } } return }
func TestIncrementUsageOnUpdateIgnoresNonPodResources(t *testing.T) { testCase := []struct { kind unversioned.GroupKind resource unversioned.GroupResource subresource string object runtime.Object }{ { kind: api.Kind("Service"), resource: api.Resource("services"), object: &api.Service{}, }, { kind: api.Kind("ReplicationController"), resource: api.Resource("replicationcontrollers"), object: &api.ReplicationController{}, }, { kind: api.Kind("ResourceQuota"), resource: api.Resource("resourcequotas"), object: &api.ResourceQuota{}, }, { kind: api.Kind("Secret"), resource: api.Resource("secrets"), object: &api.Secret{}, }, { kind: api.Kind("PersistentVolumeClaim"), resource: api.Resource("persistentvolumeclaims"), object: &api.PersistentVolumeClaim{}, }, } for _, testCase := range testCase { client := fake.NewSimpleClientset() status := &api.ResourceQuotaStatus{ Hard: api.ResourceList{}, Used: api.ResourceList{}, } r := resourceToResourceName[testCase.resource] status.Hard[r] = resource.MustParse("2") status.Used[r] = resource.MustParse("1") attributesRecord := admission.NewAttributesRecord(testCase.object, testCase.kind, "my-ns", "new-thing", testCase.resource, testCase.subresource, admission.Update, nil) dirty, err := IncrementUsage(attributesRecord, status, client) if err != nil { t.Errorf("Increment usage of resource %v had unexpected error: %v", testCase.resource, err) } if dirty { t.Errorf("Increment usage of resource %v should not result in a dirty quota on update", testCase.resource) } } }
func TestAllowsReferencedSecret(t *testing.T) { ns := "myns" admit := NewServiceAccount(nil) admit.LimitSecretReferences = true admit.RequireAPIToken = false // Add the default service account for the ns with a secret reference into the cache admit.serviceAccounts.Add(&api.ServiceAccount{ ObjectMeta: api.ObjectMeta{ Name: DefaultServiceAccountName, Namespace: ns, }, Secrets: []api.ObjectReference{ {Name: "foo"}, }, }) pod1 := &api.Pod{ Spec: api.PodSpec{ Volumes: []api.Volume{ {VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "foo"}}}, }, }, } attrs := admission.NewAttributesRecord(pod1, api.Kind("Pod"), ns, "myname", api.Resource("pods"), "", admission.Create, nil) if err := admit.Admit(attrs); err != nil { t.Errorf("Unexpected error: %v", err) } pod2 := &api.Pod{ Spec: api.PodSpec{ Containers: []api.Container{ { Name: "container-1", Env: []api.EnvVar{ { Name: "env-1", ValueFrom: &api.EnvVarSource{ SecretKeyRef: &api.SecretKeySelector{ LocalObjectReference: api.LocalObjectReference{Name: "foo"}, }, }, }, }, }, }, }, } attrs = admission.NewAttributesRecord(pod2, api.Kind("Pod"), ns, "myname", api.Resource("pods"), "", admission.Create, nil) if err := admit.Admit(attrs); err != nil { t.Errorf("Unexpected error: %v", err) } }
func resolveServiceName(f *clientcmd.Factory, resource string) (string, error) { if len(resource) == 0 { return "", fmt.Errorf("you need to provide a service name via --service") } mapper, _ := f.Object(false) rType, name, err := cmdutil.ResolveResource(kapi.Resource("services"), resource, mapper) if err != nil { return "", err } if rType != kapi.Resource("services") { return "", fmt.Errorf("cannot expose %v as routes", rType) } return name, nil }
// NewREST returns a RESTStorage object that will work against persistent volume claims. func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { store := &genericregistry.Store{ NewFunc: func() runtime.Object { return &api.PersistentVolumeClaim{} }, NewListFunc: func() runtime.Object { return &api.PersistentVolumeClaimList{} }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.PersistentVolumeClaim).Name, nil }, PredicateFunc: persistentvolumeclaim.MatchPersistentVolumeClaim, QualifiedResource: api.Resource("persistentvolumeclaims"), CreateStrategy: persistentvolumeclaim.Strategy, UpdateStrategy: persistentvolumeclaim.Strategy, DeleteStrategy: persistentvolumeclaim.Strategy, ReturnDeletedObject: true, } options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: persistentvolumeclaim.GetAttrs} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } statusStore := *store statusStore.UpdateStrategy = persistentvolumeclaim.StatusStrategy return &REST{store}, &StatusREST{store: &statusStore} }
func TestAssignsDefaultServiceAccountAndToleratesMissingAPIToken(t *testing.T) { ns := "myns" admit := NewServiceAccount(nil) admit.MountServiceAccountToken = true admit.RequireAPIToken = false // Add the default service account for the ns into the cache admit.serviceAccounts.Add(&api.ServiceAccount{ ObjectMeta: api.ObjectMeta{ Name: DefaultServiceAccountName, Namespace: ns, }, }) pod := &api.Pod{} attrs := admission.NewAttributesRecord(pod, api.Kind("Pod"), ns, "myname", api.Resource("pods"), "", admission.Create, nil) err := admit.Admit(attrs) if err != nil { t.Errorf("Unexpected error: %v", err) } if pod.Spec.ServiceAccountName != DefaultServiceAccountName { t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) } }
// NewREST returns a RESTStorage object that will work against pod templates. func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *REST { prefix := "/podtemplates" newListFunc := func() runtime.Object { return &api.PodTemplateList{} } storageInterface := storageDecorator( s, 100, &api.PodTemplate{}, prefix, false, newListFunc) store := &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.PodTemplate{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, name string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.PodTemplate).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return podtemplate.MatchPodTemplate(label, field) }, QualifiedResource: api.Resource("podtemplates"), CreateStrategy: podtemplate.Strategy, UpdateStrategy: podtemplate.Strategy, ExportStrategy: podtemplate.Strategy, ReturnDeletedObject: true, Storage: storageInterface, } return &REST{store} }
func TestAllowUnreferencedSecretVolumesForPermissiveSAs(t *testing.T) { ns := "myns" admit := NewServiceAccount(nil) admit.LimitSecretReferences = false admit.RequireAPIToken = false // Add the default service account for the ns into the cache admit.serviceAccounts.Add(&api.ServiceAccount{ ObjectMeta: api.ObjectMeta{ Name: DefaultServiceAccountName, Namespace: ns, Annotations: map[string]string{EnforceMountableSecretsAnnotation: "true"}, }, }) pod := &api.Pod{ Spec: api.PodSpec{ Volumes: []api.Volume{ {VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "foo"}}}, }, }, } attrs := admission.NewAttributesRecord(pod, api.Kind("Pod"), ns, "myname", api.Resource("pods"), "", admission.Create, nil) err := admit.Admit(attrs) if err == nil { t.Errorf("Expected rejection for using a secret the service account does not reference") } }
// NewREST returns a RESTStorage object that will work against horizontal pod autoscalers. func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *REST { prefix := "/limitranges" newListFunc := func() runtime.Object { return &api.LimitRangeList{} } storageInterface := storageDecorator( s, 100, &api.LimitRange{}, prefix, limitrange.Strategy, newListFunc) store := &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.LimitRange{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, id) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.LimitRange).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return limitrange.MatchLimitRange(label, field) }, QualifiedResource: api.Resource("limitranges"), CreateStrategy: limitrange.Strategy, UpdateStrategy: limitrange.Strategy, ExportStrategy: limitrange.Strategy, Storage: storageInterface, } return &REST{store} }
func TestRejectsUnreferencedImagePullSecrets(t *testing.T) { ns := "myns" admit := NewServiceAccount(nil) admit.LimitSecretReferences = true admit.RequireAPIToken = false // Add the default service account for the ns into the cache admit.serviceAccounts.Add(&api.ServiceAccount{ ObjectMeta: api.ObjectMeta{ Name: DefaultServiceAccountName, Namespace: ns, }, }) pod := &api.Pod{ Spec: api.PodSpec{ ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}}, }, } attrs := admission.NewAttributesRecord(pod, api.Kind("Pod"), ns, "myname", api.Resource("pods"), "", admission.Create, nil) err := admit.Admit(attrs) if err == nil { t.Errorf("Expected rejection for using a secret the service account does not reference") } }
// NewREST returns a RESTStorage object that will work against events. func NewREST(opts generic.RESTOptions, ttl uint64) *REST { prefix := "/" + opts.ResourcePrefix // We explicitly do NOT do any decoration here - switching on Cacher // for events will lead to too high memory consumption. storageInterface, dFunc := generic.NewRawStorage(opts.StorageConfig) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.Event{} }, NewListFunc: func() runtime.Object { return &api.EventList{} }, KeyRootFunc: func(ctx api.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return registry.NamespaceKeyFunc(ctx, prefix, id) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Event).Name, nil }, PredicateFunc: event.MatchEvent, TTLFunc: func(runtime.Object, uint64, bool) (uint64, error) { return ttl, nil }, QualifiedResource: api.Resource("events"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: event.Strategy, UpdateStrategy: event.Strategy, DeleteStrategy: event.Strategy, Storage: storageInterface, DestroyFunc: dFunc, } return &REST{store} }
func (tw *baseTimeoutWriter) timeout(msg string) { tw.mu.Lock() defer tw.mu.Unlock() tw.timedOut = true // The timeout writer has not been used by the inner handler. // We can safely timeout the HTTP request by sending by a timeout // handler if !tw.wroteHeader && !tw.hijacked { tw.w.WriteHeader(http.StatusGatewayTimeout) if msg != "" { tw.w.Write([]byte(msg)) } else { enc := json.NewEncoder(tw.w) enc.Encode(errors.NewServerTimeout(api.Resource(""), "", 0)) } } else { // The timeout writer has been used by the inner handler. There is // no way to timeout the HTTP request at the point. We have to shutdown // the connection for HTTP1 or reset stream for HTTP2. // // Note from: Brad Fitzpatrick // if the ServeHTTP goroutine panics, that will do the best possible thing for both // HTTP/1 and HTTP/2. In HTTP/1, assuming you're replying with at least HTTP/1.1 and // you've already flushed the headers so it's using HTTP chunking, it'll kill the TCP // connection immediately without a proper 0-byte EOF chunk, so the peer will recognize // the response as bogus. In HTTP/2 the server will just RST_STREAM the stream, leaving // the TCP connection open, but resetting the stream to the peer so it'll have an error, // like the HTTP/1 case. panic(errConnKilled) } }
// Admit determines if the service should be admitted based on the configured network CIDR. func (r *externalIPRanger) Admit(a kadmission.Attributes) error { if a.GetResource() != kapi.Resource("services") { return nil } svc, ok := a.GetObject().(*kapi.Service) // if we can't convert then we don't handle this object so just return if !ok { return nil } var errs field.ErrorList switch { // administrator disabled externalIPs case len(svc.Spec.ExternalIPs) > 0 && len(r.admit) == 0: errs = append(errs, field.Forbidden(field.NewPath("spec", "externalIPs"), "externalIPs have been disabled")) // administrator has limited the range case len(svc.Spec.ExternalIPs) > 0 && len(r.admit) > 0: for i, s := range svc.Spec.ExternalIPs { ip := net.ParseIP(s) if ip == nil { errs = append(errs, field.Forbidden(field.NewPath("spec", "externalIPs").Index(i), "externalIPs must be a valid address")) continue } if networkSlice(r.reject).Contains(ip) || !networkSlice(r.admit).Contains(ip) { errs = append(errs, field.Forbidden(field.NewPath("spec", "externalIPs").Index(i), "externalIP is not allowed")) continue } } } if len(errs) > 0 { return apierrs.NewInvalid(a.GetKind(), a.GetName(), errs) } return nil }
func TestCreateMissingDeployment(t *testing.T) { oc := &testclient.Fake{} oc.AddReactor("get", "deploymentconfigs", func(action ktestclient.Action) (handled bool, ret runtime.Object, err error) { return true, deploytest.OkDeploymentConfig(2), nil }) kc := &ktestclient.Fake{} kc.AddReactor("get", "replicationcontrollers", func(action ktestclient.Action) (handled bool, ret runtime.Object, err error) { deployment, _ := deployutil.MakeDeployment(deploytest.OkDeploymentConfig(1), codec) return true, nil, kerrors.NewNotFound(kapi.Resource("replicationController"), deployment.Name) }) obj, err := NewREST(oc, kc, codec).Create(kapi.NewDefaultContext(), &deployapi.DeploymentConfigRollback{ Name: "config", Spec: deployapi.DeploymentConfigRollbackSpec{ Revision: 1, }, }) if err == nil { t.Errorf("Expected an error") } if obj != nil { t.Error("Unexpected result obj") } }
func TestDoNotAddImagePullSecrets(t *testing.T) { ns := "myns" admit := NewServiceAccount(nil) admit.LimitSecretReferences = true admit.RequireAPIToken = false // Add the default service account for the ns with a secret reference into the cache admit.serviceAccounts.Add(&api.ServiceAccount{ ObjectMeta: api.ObjectMeta{ Name: DefaultServiceAccountName, Namespace: ns, }, ImagePullSecrets: []api.LocalObjectReference{ {Name: "foo"}, {Name: "bar"}, }, }) pod := &api.Pod{ Spec: api.PodSpec{ ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}}, }, } attrs := admission.NewAttributesRecord(pod, api.Kind("Pod"), ns, "myname", api.Resource("pods"), "", admission.Create, nil) err := admit.Admit(attrs) if err != nil { t.Errorf("Unexpected error: %v", err) } if len(pod.Spec.ImagePullSecrets) != 1 || pod.Spec.ImagePullSecrets[0].Name != "foo" { t.Errorf("unexpected image pull secrets: %v", pod.Spec.ImagePullSecrets) } }
// createDockerPullSecretReference updates a service account to reference the dockercfgSecret as a Secret and an ImagePullSecret func (e *DockercfgController) createDockerPullSecretReference(staleServiceAccount *api.ServiceAccount, dockercfgSecretName string) error { liveServiceAccount, err := e.client.ServiceAccounts(staleServiceAccount.Namespace).Get(staleServiceAccount.Name) if err != nil { return err } mountableDockercfgSecrets, imageDockercfgPullSecrets := getGeneratedDockercfgSecretNames(liveServiceAccount) staleDockercfgMountableSecrets, staleImageDockercfgPullSecrets := getGeneratedDockercfgSecretNames(staleServiceAccount) // if we're trying to create a reference based on stale lists of dockercfg secrets, let the caller know if !reflect.DeepEqual(staleDockercfgMountableSecrets.List(), mountableDockercfgSecrets.List()) || !reflect.DeepEqual(staleImageDockercfgPullSecrets.List(), imageDockercfgPullSecrets.List()) { return kapierrors.NewConflict(api.Resource("serviceaccount"), staleServiceAccount.Name, fmt.Errorf("cannot add reference to %s based on stale data. decision made for %v,%v, but live version is %v,%v", dockercfgSecretName, staleDockercfgMountableSecrets.List(), staleImageDockercfgPullSecrets.List(), mountableDockercfgSecrets.List(), imageDockercfgPullSecrets.List())) } changed := false if !mountableDockercfgSecrets.Has(dockercfgSecretName) { liveServiceAccount.Secrets = append(liveServiceAccount.Secrets, api.ObjectReference{Name: dockercfgSecretName}) changed = true } if !imageDockercfgPullSecrets.Has(dockercfgSecretName) { liveServiceAccount.ImagePullSecrets = append(liveServiceAccount.ImagePullSecrets, api.LocalObjectReference{Name: dockercfgSecretName}) changed = true } if changed { if _, err = e.client.ServiceAccounts(liveServiceAccount.Namespace).Update(liveServiceAccount); err != nil { // TODO: retry on API conflicts in case the conflict was unrelated to our generated dockercfg secrets? return err } } return nil }
func (c *mockControllerClient) GetPersistentVolumeClaim(namespace, name string) (*api.PersistentVolumeClaim, error) { if c.claim != nil { return c.claim, nil } else { return nil, errors.NewNotFound(api.Resource("persistentvolumes"), name) } }
// NewREST returns a RESTStorage object that will work against events. func NewREST(optsGetter generic.RESTOptionsGetter, ttl uint64) *REST { resource := api.Resource("events") opts, err := optsGetter.GetRESTOptions(resource) if err != nil { panic(err) // TODO: Propagate error up } // We explicitly do NOT do any decoration here - switching on Cacher // for events will lead to too high memory consumption. opts.Decorator = generic.UndecoratedStorage // TODO use watchCacheSize=-1 to signal UndecoratedStorage store := &genericregistry.Store{ NewFunc: func() runtime.Object { return &api.Event{} }, NewListFunc: func() runtime.Object { return &api.EventList{} }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Event).Name, nil }, PredicateFunc: event.MatchEvent, TTLFunc: func(runtime.Object, uint64, bool) (uint64, error) { return ttl, nil }, QualifiedResource: resource, CreateStrategy: event.Strategy, UpdateStrategy: event.Strategy, DeleteStrategy: event.Strategy, } options := &generic.StoreOptions{RESTOptions: opts, AttrFunc: event.GetAttrs} // Pass in opts to use UndecoratedStorage if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } return &REST{store} }
func TestIgnoresNilObject(t *testing.T) { attrs := admission.NewAttributesRecord(nil, api.Kind("Pod"), "myns", "myname", api.Resource("pods"), "", admission.Create, nil) err := NewServiceAccount(nil).Admit(attrs) if err != nil { t.Errorf("Expected nil object allowed allowed, got err: %v", err) } }
func (f *podInformer) Informer() framework.SharedIndexInformer { f.lock.Lock() defer f.lock.Unlock() informerObj := &kapi.Pod{} informerType := reflect.TypeOf(informerObj) informer, exists := f.informers[informerType] if exists { return informer } lw := f.customListerWatchers.GetListerWatcher(kapi.Resource("pods")) if lw == nil { lw = &cache.ListWatch{ ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { return f.kubeClient.Pods(kapi.NamespaceAll).List(options) }, WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { return f.kubeClient.Pods(kapi.NamespaceAll).Watch(options) }, } } informer = framework.NewSharedIndexInformer( lw, informerObj, f.defaultResync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, ) f.informers[informerType] = informer return informer }
func TestCheckGeneratedNameError(t *testing.T) { expect := errors.NewNotFound(api.Resource("foos"), "bar") if err := rest.CheckGeneratedNameError(Strategy, expect, &api.Pod{}); err != expect { t.Errorf("NotFoundError should be ignored: %v", err) } expect = errors.NewAlreadyExists(api.Resource("foos"), "bar") if err := rest.CheckGeneratedNameError(Strategy, expect, &api.Pod{}); err != expect { t.Errorf("AlreadyExists should be returned when no GenerateName field: %v", err) } expect = errors.NewAlreadyExists(api.Resource("foos"), "bar") if err := rest.CheckGeneratedNameError(Strategy, expect, &api.Pod{ObjectMeta: api.ObjectMeta{GenerateName: "foo"}}); err == nil || !errors.IsServerTimeout(err) { t.Errorf("expected try again later error: %v", err) } }
// NewREST returns a RESTStorage object that will work against endpoints. func NewREST(opts generic.RESTOptions) *REST { prefix := "/services/endpoints" newListFunc := func() runtime.Object { return &api.EndpointsList{} } storageInterface := opts.Decorator( opts.Storage, cachesize.GetWatchCacheSizeByResource(cachesize.Endpoints), &api.Endpoints{}, prefix, endpoint.Strategy, newListFunc) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.Endpoints{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, name string) (string, error) { return registry.NamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Endpoints).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return endpoint.MatchEndpoints(label, field) }, QualifiedResource: api.Resource("endpoints"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: endpoint.Strategy, UpdateStrategy: endpoint.Strategy, DeleteStrategy: endpoint.Strategy, Storage: storageInterface, } return &REST{store} }
func (f *clusterResourceQuotaInformer) Informer() framework.SharedIndexInformer { f.lock.Lock() defer f.lock.Unlock() informerObj := "aapi.ClusterResourceQuota{} informerType := reflect.TypeOf(informerObj) informer, exists := f.informers[informerType] if exists { return informer } lw := f.customListerWatchers.GetListerWatcher(kapi.Resource("clusterresourcequotas")) if lw == nil { lw = &cache.ListWatch{ ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { return f.originClient.ClusterResourceQuotas().List(options) }, WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { return f.originClient.ClusterResourceQuotas().Watch(options) }, } } informer = framework.NewSharedIndexInformer( lw, informerObj, f.defaultResync, cache.Indexers{}, ) f.informers[informerType] = informer return informer }
// TestHandle_deployerPodAlreadyExists ensures that attempts to create a // deployer pod which was already created don't result in an error // (effectively skipping the handling as redundant). func TestHandle_deployerPodAlreadyExists(t *testing.T) { var updatedDeployment *kapi.ReplicationController config := deploytest.OkDeploymentConfig(1) deployment, _ := deployutil.MakeDeployment(config, codec) deployment.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusNew) deployerPodName := deployutil.DeployerPodNameForDeployment(deployment.Name) fake := &ktestclient.Fake{} fake.AddReactor("create", "pods", func(action ktestclient.Action) (handled bool, ret runtime.Object, err error) { name := action.(ktestclient.CreateAction).GetObject().(*kapi.Pod).Name return true, nil, kerrors.NewAlreadyExists(kapi.Resource("Pod"), name) }) fake.AddReactor("update", "replicationcontrollers", func(action ktestclient.Action) (handled bool, ret runtime.Object, err error) { rc := action.(ktestclient.UpdateAction).GetObject().(*kapi.ReplicationController) updatedDeployment = rc return true, rc, nil }) controller := okDeploymentController(fake, deployment, nil, true) if err := controller.Handle(deployment); err != nil { t.Fatalf("unexpected error: %v", err) } if updatedDeployment.Annotations[deployapi.DeploymentPodAnnotation] != deployerPodName { t.Fatalf("deployment not updated with pod name annotation") } if updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation] != string(deployapi.DeploymentStatusPending) { t.Fatalf("deployment status not updated to pending") } }
// NewREST returns a RESTStorage object that will work against horizontal pod autoscalers. func NewREST(opts generic.RESTOptions) *REST { prefix := "/limitranges" newListFunc := func() runtime.Object { return &api.LimitRangeList{} } storageInterface := opts.Decorator( opts.Storage, cachesize.GetWatchCacheSizeByResource(cachesize.LimitRanges), &api.LimitRange{}, prefix, limitrange.Strategy, newListFunc) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.LimitRange{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return registry.NamespaceKeyFunc(ctx, prefix, id) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.LimitRange).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return limitrange.MatchLimitRange(label, field) }, QualifiedResource: api.Resource("limitranges"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: limitrange.Strategy, UpdateStrategy: limitrange.Strategy, DeleteStrategy: limitrange.Strategy, ExportStrategy: limitrange.Strategy, Storage: storageInterface, } return &REST{store} }
// NewREST returns a RESTStorage object that will work against services. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { prefix := "/services/specs" newListFunc := func() runtime.Object { return &api.ServiceList{} } storageInterface := opts.Decorator( opts.Storage, cachesize.GetWatchCacheSizeByResource(cachesize.Services), &api.Service{}, prefix, service.Strategy, newListFunc) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.Service{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, name string) (string, error) { return registry.NamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Service).Name, nil }, PredicateFunc: service.MatchServices, QualifiedResource: api.Resource("services"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: service.Strategy, UpdateStrategy: service.Strategy, DeleteStrategy: service.Strategy, ExportStrategy: service.Strategy, Storage: storageInterface, } statusStore := *store statusStore.UpdateStrategy = service.StatusStrategy return &REST{store}, &StatusREST{store: &statusStore} }
// NewREST returns a RESTStorage object that will work against pod templates. func NewREST(opts generic.RESTOptions) *REST { prefix := "/podtemplates" newListFunc := func() runtime.Object { return &api.PodTemplateList{} } storageInterface := opts.Decorator( opts.Storage, cachesize.GetWatchCacheSizeByResource(cachesize.PodTemplates), &api.PodTemplate{}, prefix, podtemplate.Strategy, newListFunc) store := &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.PodTemplate{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, name string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.PodTemplate).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return podtemplate.MatchPodTemplate(label, field) }, QualifiedResource: api.Resource("podtemplates"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: podtemplate.Strategy, UpdateStrategy: podtemplate.Strategy, ExportStrategy: podtemplate.Strategy, ReturnDeletedObject: true, Storage: storageInterface, } return &REST{store} }