diff --git a/pkg/cmd/cli/describe/printer.go b/pkg/cmd/cli/describe/printer.go index 366fe28a6bac..945fcfcc19be 100644 --- a/pkg/cmd/cli/describe/printer.go +++ b/pkg/cmd/cli/describe/printer.go @@ -25,7 +25,7 @@ var ( buildConfigColumns = []string{"NAME", "TYPE", "SOURCE"} imageColumns = []string{"NAME", "DOCKER REF"} imageRepositoryColumns = []string{"NAME", "DOCKER REPO", "TAGS"} - projectColumns = []string{"NAME", "DISPLAY NAME", "NAMESPACE"} + projectColumns = []string{"NAME", "DISPLAY NAME"} routeColumns = []string{"NAME", "HOST/PORT", "PATH", "SERVICE", "LABELS"} deploymentColumns = []string{"NAME", "STATUS", "CAUSE"} deploymentConfigColumns = []string{"NAME", "TRIGGERS", "LATEST VERSION"} @@ -163,7 +163,7 @@ func printImageRepositoryList(repos *imageapi.ImageRepositoryList, w io.Writer) } func printProject(project *projectapi.Project, w io.Writer) error { - _, err := fmt.Fprintf(w, "%s\t%s\t%s\n", project.Name, project.DisplayName, project.Namespace) + _, err := fmt.Fprintf(w, "%s\t%s\n", project.Name, project.DisplayName) return err } diff --git a/pkg/cmd/server/origin/master.go b/pkg/cmd/server/origin/master.go index 5dda133d67d4..34bc77693fb9 100644 --- a/pkg/cmd/server/origin/master.go +++ b/pkg/cmd/server/origin/master.go @@ -60,7 +60,6 @@ import ( clientregistry "github.com/openshift/origin/pkg/oauth/registry/client" clientauthorizationregistry "github.com/openshift/origin/pkg/oauth/registry/clientauthorization" oauthetcd "github.com/openshift/origin/pkg/oauth/registry/etcd" - projectetcd "github.com/openshift/origin/pkg/project/registry/etcd" projectregistry "github.com/openshift/origin/pkg/project/registry/project" routeetcd "github.com/openshift/origin/pkg/route/registry/etcd" routeregistry "github.com/openshift/origin/pkg/route/registry/route" @@ -249,7 +248,6 @@ func (c *MasterConfig) InstallProtectedAPI(container *restful.Container) []strin imageEtcd := imageetcd.New(c.EtcdHelper, imageetcd.DefaultRegistryFunc(defaultRegistryFunc)) deployEtcd := deployetcd.New(c.EtcdHelper) routeEtcd := routeetcd.New(c.EtcdHelper) - projectEtcd := projectetcd.New(c.EtcdHelper) userEtcd := useretcd.New(c.EtcdHelper, user.NewDefaultUserInitStrategy()) oauthEtcd := oauthetcd.New(c.EtcdHelper) authorizationEtcd := authorizationetcd.New(c.EtcdHelper) @@ -291,7 +289,7 @@ func (c *MasterConfig) InstallProtectedAPI(container *restful.Container) []strin "routes": routeregistry.NewREST(routeEtcd), - "projects": projectregistry.NewREST(projectEtcd), + "projects": projectregistry.NewREST(kclient.Namespaces()), "userIdentityMappings": useridentitymapping.NewREST(userEtcd), "users": userregistry.NewREST(userEtcd), diff --git a/pkg/project/registry/etcd/etcd.go b/pkg/project/registry/etcd/etcd.go deleted file mode 100644 index 9031cff17ecc..000000000000 --- a/pkg/project/registry/etcd/etcd.go +++ /dev/null @@ -1,82 +0,0 @@ -package etcd - -import ( - "errors" - - kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd" - "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" - "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" - - "github.com/openshift/origin/pkg/project/api" -) - -const ( - // ProjectPath is the path to project resources in etcd - ProjectPath string = "/projects" -) - -// Etcd implements ProjectRegistry and ProjectRepositoryRegistry backed by etcd. -type Etcd struct { - tools.EtcdHelper -} - -// New returns a new etcd registry. -func New(helper tools.EtcdHelper) *Etcd { - return &Etcd{ - EtcdHelper: helper, - } -} - -// makeProjectListKey constructs etcd paths to project directories -func makeProjectListKey(ctx kapi.Context) string { - return ProjectPath -} - -// makeProjectKey constructs etcd paths to project items -func makeProjectKey(ctx kapi.Context, id string) string { - return makeProjectListKey(ctx) + "/" + id -} - -// ListProjects retrieves a list of projects that match selector. -func (r *Etcd) ListProjects(ctx kapi.Context, selector labels.Selector) (*api.ProjectList, error) { - list := api.ProjectList{} - err := r.ExtractToList(makeProjectListKey(ctx), &list) - if err != nil { - return nil, err - } - filtered := []api.Project{} - for _, item := range list.Items { - if selector.Matches(labels.Set(item.Labels)) { - filtered = append(filtered, item) - } - } - list.Items = filtered - return &list, nil -} - -// GetProject retrieves a specific project -func (r *Etcd) GetProject(ctx kapi.Context, id string) (*api.Project, error) { - var project api.Project - if err := r.ExtractObj(makeProjectKey(ctx, id), &project, false); err != nil { - return nil, etcderr.InterpretGetError(err, "project", id) - } - return &project, nil -} - -// CreateProject creates a new project -func (r *Etcd) CreateProject(ctx kapi.Context, project *api.Project) error { - err := r.CreateObj(makeProjectKey(ctx, project.Name), project, 0) - return etcderr.InterpretCreateError(err, "project", project.Name) -} - -// UpdateProject updates an existing project -func (r *Etcd) UpdateProject(ctx kapi.Context, project *api.Project) error { - return errors.New("not supported") -} - -// DeleteProject deletes an existing project -func (r *Etcd) DeleteProject(ctx kapi.Context, id string) error { - err := r.Delete(makeProjectKey(ctx, id), false) - return etcderr.InterpretDeleteError(err, "project", id) -} diff --git a/pkg/project/registry/etcd/etcd_test.go b/pkg/project/registry/etcd/etcd_test.go deleted file mode 100644 index a3fb2ffc51ae..000000000000 --- a/pkg/project/registry/etcd/etcd_test.go +++ /dev/null @@ -1,279 +0,0 @@ -package etcd - -import ( - "fmt" - "testing" - - kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" - "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" - "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" - "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" - "github.com/coreos/go-etcd/etcd" - - "github.com/openshift/origin/pkg/api/latest" - "github.com/openshift/origin/pkg/project/api" -) - -func NewTestEtcd(client tools.EtcdClient) *Etcd { - return New(tools.EtcdHelper{client, latest.Codec, tools.RuntimeVersionAdapter{latest.ResourceVersioner}}) -} - -func TestEtcdListProjectsEmpty(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - key := makeProjectListKey(ctx) - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{}, - }, - }, - E: nil, - } - registry := NewTestEtcd(fakeClient) - projects, err := registry.ListProjects(ctx, labels.Everything()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if len(projects.Items) != 0 { - t.Errorf("Unexpected projects list: %#v", projects) - } -} - -func TestEtcdListProjectsError(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - key := makeProjectListKey(ctx) - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: nil, - }, - E: fmt.Errorf("some error"), - } - registry := NewTestEtcd(fakeClient) - projects, err := registry.ListProjects(ctx, labels.Everything()) - if err == nil { - t.Error("unexpected nil error") - } - - if projects != nil { - t.Errorf("Unexpected non-nil projects: %#v", projects) - } -} - -func TestEtcdListProjectsEverything(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - key := makeProjectListKey(ctx) - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - { - Value: runtime.EncodeOrDie(latest.Codec, &api.Project{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), - }, - { - Value: runtime.EncodeOrDie(latest.Codec, &api.Project{ObjectMeta: kapi.ObjectMeta{Name: "bar"}}), - }, - }, - }, - }, - E: nil, - } - registry := NewTestEtcd(fakeClient) - projects, err := registry.ListProjects(ctx, labels.Everything()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if len(projects.Items) != 2 || projects.Items[0].Name != "foo" || projects.Items[1].Name != "bar" { - t.Errorf("Unexpected projects list: %#v", projects) - } -} - -func TestEtcdListProjectsFiltered(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - key := makeProjectListKey(ctx) - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - { - Value: runtime.EncodeOrDie(latest.Codec, &api.Project{ - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"env": "prod"}, - }, - }), - }, - { - Value: runtime.EncodeOrDie(latest.Codec, &api.Project{ - ObjectMeta: kapi.ObjectMeta{ - Name: "bar", - Labels: map[string]string{"env": "dev"}, - }, - }), - }, - }, - }, - }, - E: nil, - } - registry := NewTestEtcd(fakeClient) - projects, err := registry.ListProjects(ctx, labels.SelectorFromSet(labels.Set{"env": "dev"})) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if len(projects.Items) != 1 || projects.Items[0].Name != "bar" { - t.Errorf("Unexpected projects list: %#v", projects) - } -} - -func TestEtcdGetProject(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Set(makeProjectKey(ctx, "foo"), runtime.EncodeOrDie(latest.Codec, &api.Project{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0) - registry := NewTestEtcd(fakeClient) - project, err := registry.GetProject(ctx, "foo") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if project.Name != "foo" { - t.Errorf("Unexpected project: %#v", project) - } -} - -func TestEtcdGetProjectNotFound(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Data[makeProjectKey(ctx, "foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: nil, - }, - E: tools.EtcdErrorNotFound, - } - registry := NewTestEtcd(fakeClient) - project, err := registry.GetProject(ctx, "foo") - if err == nil { - t.Errorf("Unexpected non-error.") - } - if project != nil { - t.Errorf("Unexpected project: %#v", project) - } -} - -func TestEtcdCreateProject(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.TestIndex = true - fakeClient.Data[makeProjectKey(ctx, "foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: nil, - }, - E: tools.EtcdErrorNotFound, - } - registry := NewTestEtcd(fakeClient) - err := registry.CreateProject(ctx, &api.Project{ - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - }, - }) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - resp, err := fakeClient.Get(makeProjectKey(ctx, "foo"), false, false) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - var project api.Project - err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &project) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if project.Name != "foo" { - t.Errorf("Unexpected project: %#v %s", project, resp.Node.Value) - } -} - -func TestEtcdCreateProjectAlreadyExists(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Data[makeProjectKey(ctx, "foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &api.Project{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), - }, - }, - E: nil, - } - registry := NewTestEtcd(fakeClient) - err := registry.CreateProject(ctx, &api.Project{ - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - }, - }) - if err == nil { - t.Error("Unexpected non-error") - } - if !errors.IsAlreadyExists(err) { - t.Errorf("Expected 'already exists' error, got %#v", err) - } -} - -func TestEtcdUpdateProject(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - registry := NewTestEtcd(fakeClient) - err := registry.UpdateProject(ctx, &api.Project{}) - if err == nil { - t.Error("Unexpected non-error") - } -} - -func TestEtcdDeleteProjectNotFound(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Err = tools.EtcdErrorNotFound - registry := NewTestEtcd(fakeClient) - err := registry.DeleteProject(ctx, "foo") - if err == nil { - t.Error("Unexpected non-error") - } - if !errors.IsNotFound(err) { - t.Errorf("Expected 'not found' error, got %#v", err) - } -} - -func TestEtcdDeleteProjectError(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Err = fmt.Errorf("Some error") - registry := NewTestEtcd(fakeClient) - err := registry.DeleteProject(ctx, "foo") - if err == nil { - t.Error("Unexpected non-error") - } -} - -func TestEtcdDeleteProjectOK(t *testing.T) { - ctx := kapi.NewContext() - fakeClient := tools.NewFakeEtcdClient(t) - registry := NewTestEtcd(fakeClient) - key := makeProjectKey(ctx, "foo") - err := registry.DeleteProject(ctx, "foo") - if err != nil { - t.Errorf("Unexpected error: %#v", err) - } - if len(fakeClient.DeletedKeys) != 1 { - t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys) - } else if fakeClient.DeletedKeys[0] != key { - t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key) - } -} diff --git a/pkg/project/registry/project/registry.go b/pkg/project/registry/project/registry.go deleted file mode 100644 index dd38de21aacf..000000000000 --- a/pkg/project/registry/project/registry.go +++ /dev/null @@ -1,22 +0,0 @@ -package project - -import ( - kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" - "github.com/openshift/origin/pkg/project/api" -) - -// Registry is an interface for things that know how to store Project objects. -type Registry interface { - // ListProjects obtains a list of Projects that match a selector. - ListProjects(ctx kapi.Context, selector labels.Selector) (*api.ProjectList, error) - // GetProject retrieves a specific Project. - GetProject(ctx kapi.Context, id string) (*api.Project, error) - // CreateProject creates a new Project. - CreateProject(ctx kapi.Context, Project *api.Project) error - // UpdateProject updates an Project. - UpdateProject(ctx kapi.Context, Project *api.Project) error - // DeleteProject deletes an Project. - DeleteProject(ctx kapi.Context, id string) error -} diff --git a/pkg/project/registry/project/rest.go b/pkg/project/registry/project/rest.go index e2a7e08df829..b59b7dfdb1bb 100644 --- a/pkg/project/registry/project/rest.go +++ b/pkg/project/registry/project/rest.go @@ -6,6 +6,7 @@ import ( kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" + kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" @@ -15,12 +16,12 @@ import ( // REST implements the RESTStorage interface in terms of an Registry. type REST struct { - registry Registry + client kclient.NamespaceInterface } -// NewStorage returns a new REST. -func NewREST(registry Registry) apiserver.RESTStorage { - return &REST{registry} +// NewREST returns a RESTStorage object that will work against Project resources +func NewREST(client kclient.NamespaceInterface) apiserver.RESTStorage { + return &REST{client: client} } // New returns a new Project for use with Create and Update. @@ -28,27 +29,57 @@ func (s *REST) New() runtime.Object { return &api.Project{} } +// NewList returns a new ProjectList func (*REST) NewList() runtime.Object { - return &api.Project{} + return &api.ProjectList{} +} + +// convertNamespace transforms a Namespace into a Project +func convertNamespace(namespace *kapi.Namespace) *api.Project { + displayName := namespace.Annotations["displayname"] + return &api.Project{ + ObjectMeta: namespace.ObjectMeta, + DisplayName: displayName, + } +} + +// convertProject transforms a Project into a Namespace +func convertProject(project *api.Project) *kapi.Namespace { + namespace := &kapi.Namespace{ + ObjectMeta: project.ObjectMeta, + } + if namespace.Annotations == nil { + namespace.Annotations = map[string]string{} + } + namespace.Annotations["displayname"] = project.DisplayName + return namespace +} + +// convertNamespaceList transforms a NamespaceList into a ProjectList +func convertNamespaceList(namespaceList *kapi.NamespaceList) *api.ProjectList { + projects := &api.ProjectList{} + for _, n := range namespaceList.Items { + projects.Items = append(projects.Items, *convertNamespace(&n)) + } + return projects } // List retrieves a list of Projects that match selector. func (s *REST) List(ctx kapi.Context, selector, fields labels.Selector) (runtime.Object, error) { - projects, err := s.registry.ListProjects(ctx, selector) + namespaces, err := s.client.List(selector) if err != nil { return nil, err } - - return projects, nil + return convertNamespaceList(namespaces), nil } -// Get retrieves an Project by id. -func (s *REST) Get(ctx kapi.Context, id string) (runtime.Object, error) { - project, err := s.registry.GetProject(ctx, id) +// Get retrieves a Project by name +func (s *REST) Get(ctx kapi.Context, name string) (runtime.Object, error) { + namespace, err := s.client.Get(name) if err != nil { return nil, err } - return project, nil + return convertNamespace(namespace), nil } // Create registers the given Project. @@ -57,22 +88,18 @@ func (s *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, err if !ok { return nil, fmt.Errorf("not a project: %#v", obj) } - kapi.FillObjectMetaSystemFields(ctx, &project.ObjectMeta) - - // kubectl auto-inserts a value, we need to ignore this value until we have cluster-scoped actions in kubectl - project.Namespace = "" if errs := validation.ValidateProject(project); len(errs) > 0 { return nil, errors.NewInvalid("project", project.Name, errs) } - - if err := s.registry.CreateProject(ctx, project); err != nil { + namespace, err := s.client.Create(convertProject(project)) + if err != nil { return nil, err } - return s.Get(ctx, project.Name) + return convertNamespace(namespace), nil } -// Delete deletes a Project specified by its id. -func (s *REST) Delete(ctx kapi.Context, id string) (runtime.Object, error) { - return &kapi.Status{Status: kapi.StatusSuccess}, s.registry.DeleteProject(ctx, id) +// Delete deletes a Project specified by its name +func (s *REST) Delete(ctx kapi.Context, name string) (runtime.Object, error) { + return &kapi.Status{Status: kapi.StatusSuccess}, s.client.Delete(name) } diff --git a/pkg/project/registry/project/rest_test.go b/pkg/project/registry/project/rest_test.go index 3c04051d1c25..7a15576116f7 100644 --- a/pkg/project/registry/project/rest_test.go +++ b/pkg/project/registry/project/rest_test.go @@ -1,85 +1,42 @@ package project import ( - "fmt" + // "fmt" "strings" "testing" kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" - "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" + //"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/openshift/origin/pkg/project/api" - "github.com/openshift/origin/pkg/project/registry/test" ) -func TestListProjectsError(t *testing.T) { - mockRegistry := test.NewProjectRegistry() - mockRegistry.Err = fmt.Errorf("test error") - - storage := REST{ - registry: mockRegistry, - } - - projects, err := storage.List(nil, nil, nil) - if err != mockRegistry.Err { - t.Errorf("Expected %#v, Got %#v", mockRegistry.Err, err) - } - - if projects != nil { - t.Errorf("Unexpected non-nil projects list: %#v", projects) - } -} - -func TestListProjectsEmptyList(t *testing.T) { - mockRegistry := test.NewProjectRegistry() - mockRegistry.Projects = &api.ProjectList{ - Items: []api.Project{}, - } - - storage := REST{ - registry: mockRegistry, - } - - projects, err := storage.List(nil, labels.Everything(), labels.Everything()) - if err != nil { - t.Errorf("Unexpected non-nil error: %#v", err) - } - - if len(projects.(*api.ProjectList).Items) != 0 { - t.Errorf("Unexpected non-zero projects list: %#v", projects) - } -} - -func TestListProjectsPopulatedList(t *testing.T) { - mockRegistry := test.NewProjectRegistry() - mockRegistry.Projects = &api.ProjectList{ - Items: []api.Project{ - { - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - }, - }, +func TestListProjects(t *testing.T) { + namespaceList := kapi.NamespaceList{ + Items: []kapi.Namespace{ { - ObjectMeta: kapi.ObjectMeta{ - Name: "bar", - }, + ObjectMeta: kapi.ObjectMeta{Name: "foo"}, }, }, } - + mockClient := &kclient.Fake{ + NamespacesList: namespaceList, + } storage := REST{ - registry: mockRegistry, + client: mockClient.Namespaces(), } - - list, err := storage.List(nil, labels.Everything(), labels.Everything()) + response, err := storage.List(nil, nil, nil) if err != nil { - t.Errorf("Unexpected non-nil error: %#v", err) + t.Errorf("%#v should be nil.", err) } - - projects := list.(*api.ProjectList) - - if e, a := 2, len(projects.Items); e != a { - t.Errorf("Expected %v, got %v", e, a) + projects := response.(*api.ProjectList) + if len(projects.Items) != 1 { + t.Errorf("%#v projects.Items should have len 1.", projects.Items) + } + responseProject := projects.Items[0] + if e, r := responseProject.Name, "foo"; e != r { + t.Errorf("%#v != %#v.", e, r) } } @@ -107,63 +64,54 @@ func TestCreateProjectMissingID(t *testing.T) { } } -func TestCreateRegistrySaveError(t *testing.T) { - mockRegistry := test.NewProjectRegistry() - mockRegistry.Err = fmt.Errorf("test error") - storage := REST{registry: mockRegistry} - - _, err := storage.Create(nil, &api.Project{ - ObjectMeta: kapi.ObjectMeta{Name: "foo"}, - }) - if err != mockRegistry.Err { - t.Errorf("Unexpected non-nil error: %#v", err) - } -} - func TestCreateProjectOK(t *testing.T) { - mockRegistry := test.NewProjectRegistry() - storage := REST{registry: mockRegistry} - - obj, err := storage.Create(nil, &api.Project{ + mockClient := &kclient.Fake{} + storage := REST{ + client: mockClient.Namespaces(), + } + _, err := storage.Create(nil, &api.Project{ ObjectMeta: kapi.ObjectMeta{Name: "foo"}, }) - if obj == nil { - t.Errorf("Expected nil obj, got %v", obj) - } if err != nil { t.Errorf("Unexpected non-nil error: %#v", err) } - - project, ok := obj.(*api.Project) - if !ok { - t.Errorf("Expected project type, got: %#v", obj) + if len(mockClient.Actions) != 1 { + t.Errorf("Expected client action for create") } - if project.Name != "foo" { - t.Errorf("Unexpected project: %#v", project) + if mockClient.Actions[0].Action != "create-namespace" { + t.Errorf("Expected call to create-namespace") } + + /* + TODO: Need upstream change to fake_namespaces so create returns the object passed in on client and not nil object + project, ok := obj.(*api.Project) + if !ok { + t.Errorf("Expected project type, got: %#v", obj) + } + if project.Name != "foo" { + t.Errorf("Unexpected project: %#v", project) + }*/ } func TestGetProjectError(t *testing.T) { - mockRegistry := test.NewProjectRegistry() - mockRegistry.Err = fmt.Errorf("bad") - storage := REST{registry: mockRegistry} - - project, err := storage.Get(nil, "foo") - if project != nil { - t.Errorf("Unexpected non-nil project: %#v", project) - } - if err != mockRegistry.Err { - t.Errorf("Expected %#v, got %#v", mockRegistry.Err, err) - } + // TODO: Need upstream change to fake_namespaces so get returns the error on Fake + /* + mockRegistry := test.NewProjectRegistry() + mockRegistry.Err = fmt.Errorf("bad") + storage := REST{registry: mockRegistry} + + project, err := storage.Get(nil, "foo") + if project != nil { + t.Errorf("Unexpected non-nil project: %#v", project) + } + if err != mockRegistry.Err { + t.Errorf("Expected %#v, got %#v", mockRegistry.Err, err) + }*/ } func TestGetProjectOK(t *testing.T) { - mockRegistry := test.NewProjectRegistry() - mockRegistry.Project = &api.Project{ - ObjectMeta: kapi.ObjectMeta{Name: "foo"}, - } - storage := REST{registry: mockRegistry} - + mockClient := &kclient.Fake{} + storage := REST{client: mockClient.Namespaces()} project, err := storage.Get(nil, "foo") if project == nil { t.Error("Unexpected nil project") @@ -177,8 +125,10 @@ func TestGetProjectOK(t *testing.T) { } func TestDeleteProject(t *testing.T) { - mockRegistry := test.NewProjectRegistry() - storage := REST{registry: mockRegistry} + mockClient := &kclient.Fake{} + storage := REST{ + client: mockClient.Namespaces(), + } obj, err := storage.Delete(nil, "foo") if obj == nil { t.Error("Unexpected nil obj") @@ -186,7 +136,6 @@ func TestDeleteProject(t *testing.T) { if err != nil { t.Errorf("Unexpected non-nil error: %#v", err) } - status, ok := obj.(*kapi.Status) if !ok { t.Errorf("Expected status type, got: %#v", obj) @@ -194,4 +143,10 @@ func TestDeleteProject(t *testing.T) { if status.Status != kapi.StatusSuccess { t.Errorf("Expected status=success, got: %#v", status) } + if len(mockClient.Actions) != 1 { + t.Errorf("Expected client action for delete") + } + if mockClient.Actions[0].Action != "delete-namespace" { + t.Errorf("Expected call to delete-namespace") + } } diff --git a/pkg/project/registry/test/project.go b/pkg/project/registry/test/project.go deleted file mode 100644 index b09b2fcac2e6..000000000000 --- a/pkg/project/registry/test/project.go +++ /dev/null @@ -1,57 +0,0 @@ -package test - -import ( - "sync" - - kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" - "github.com/openshift/origin/pkg/project/api" -) - -type ProjectRegistry struct { - Err error - Project *api.Project - Projects *api.ProjectList - sync.Mutex -} - -func NewProjectRegistry() *ProjectRegistry { - return &ProjectRegistry{} -} - -func (r *ProjectRegistry) ListProjects(ctx kapi.Context, selector labels.Selector) (*api.ProjectList, error) { - r.Lock() - defer r.Unlock() - - return r.Projects, r.Err -} - -func (r *ProjectRegistry) GetProject(ctx kapi.Context, id string) (*api.Project, error) { - r.Lock() - defer r.Unlock() - - return r.Project, r.Err -} - -func (r *ProjectRegistry) CreateProject(ctx kapi.Context, project *api.Project) error { - r.Lock() - defer r.Unlock() - - r.Project = project - return r.Err -} - -func (r *ProjectRegistry) UpdateProject(ctx kapi.Context, project *api.Project) error { - r.Lock() - defer r.Unlock() - - r.Project = project - return r.Err -} - -func (r *ProjectRegistry) DeleteProject(ctx kapi.Context, id string) error { - r.Lock() - defer r.Unlock() - - return r.Err -} diff --git a/test/integration/project_test.go b/test/integration/project_test.go new file mode 100644 index 000000000000..3dc6bffb2eb9 --- /dev/null +++ b/test/integration/project_test.go @@ -0,0 +1,103 @@ +// +build integration,!no-etcd + +package integration + +import ( + "net/http/httptest" + "testing" + + kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + klatest "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" + kv1beta1 "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" + "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" + kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" + "github.com/GoogleCloudPlatform/kubernetes/pkg/master" + "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/namespace" + "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/admit" + + "github.com/openshift/origin/pkg/api/latest" + "github.com/openshift/origin/pkg/api/v1beta1" + "github.com/openshift/origin/pkg/client" + projectapi "github.com/openshift/origin/pkg/project/api" + projectregistry "github.com/openshift/origin/pkg/project/registry/project" +) + +func init() { + requireEtcd() +} + +// TestProjectIsNamespace verifies that a project is a namespace, and a namespace is a project +func TestProjectIsNamespace(t *testing.T) { + deleteAllEtcdKeys() + etcdClient := newEtcdClient() + etcdHelper, err := master.NewEtcdHelper(etcdClient, "") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // create a kube and its client + kubeInterfaces, _ := klatest.InterfacesFor(klatest.Version) + namespaceRegistry := namespace.NewEtcdRegistry(etcdHelper) + kubeStorage := map[string]apiserver.RESTStorage{ + "namespaces": namespace.NewREST(namespaceRegistry), + } + kubeServer := httptest.NewServer(apiserver.Handle(kubeStorage, kv1beta1.Codec, "/api", "v1beta1", kubeInterfaces.MetadataAccessor, admit.NewAlwaysAdmit(), kapi.NewRequestContextMapper(), klatest.RESTMapper)) + defer kubeServer.Close() + kubeClient, err := kclient.New(&kclient.Config{Host: kubeServer.URL}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // create an origin + originInterfaces, _ := latest.InterfacesFor(latest.Version) + originStorage := map[string]apiserver.RESTStorage{ + "projects": projectregistry.NewREST(kubeClient.Namespaces()), + } + originServer := httptest.NewServer(apiserver.Handle(originStorage, v1beta1.Codec, "/osapi", "v1beta1", originInterfaces.MetadataAccessor, admit.NewAlwaysAdmit(), kapi.NewRequestContextMapper(), latest.RESTMapper)) + defer originServer.Close() + originClient, err := client.New(&kclient.Config{Host: originServer.URL}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // create a namespace + namespace := &kapi.Namespace{ + ObjectMeta: kapi.ObjectMeta{Name: "integration-test"}, + } + namespaceResult, err := kubeClient.Namespaces().Create(namespace) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // now try to get the project with the same name and ensure it is our namespace + project, err := originClient.Projects().Get(namespaceResult.Name) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if project.Name != namespace.Name { + t.Fatalf("Project name did not match namespace name, project %v, namespace %v", project.Name, namespace.Name) + } + + // now create a project + project = &projectapi.Project{ + ObjectMeta: kapi.ObjectMeta{Name: "new-project"}, + DisplayName: "Hello World", + } + projectResult, err := originClient.Projects().Create(project) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // now get the namespace for that project + namespace, err = kubeClient.Namespaces().Get(projectResult.Name) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if project.Name != namespace.Name { + t.Fatalf("Project name did not match namespace name, project %v, namespace %v", project.Name, namespace.Name) + } + if project.DisplayName != namespace.Annotations["displayname"] { + t.Fatalf("Project display name did not match namespace annotation, project %v, namespace %v", project.DisplayName, namespace.Annotations["displayname"]) + } + +}