diff --git a/pkg/occlient/occlient.go b/pkg/occlient/occlient.go index ee06a1c27..9a6d1c0e8 100644 --- a/pkg/occlient/occlient.go +++ b/pkg/occlient/occlient.go @@ -465,7 +465,7 @@ func (c *Client) GetProjectNames() ([]string, error) { } // CreateNewProject creates project with given projectName -func (c *Client) CreateNewProject(projectName string) error { +func (c *Client) CreateNewProject(projectName string, wait bool) error { projectRequest := &projectv1.ProjectRequest{ ObjectMeta: metav1.ObjectMeta{ Name: projectName, @@ -475,6 +475,28 @@ func (c *Client) CreateNewProject(projectName string) error { if err != nil { return errors.Wrapf(err, "unable to create new project %s", projectName) } + + if wait { + w, err := c.projectClient.Projects().Watch(metav1.ListOptions{ + FieldSelector: fields.Set{"metadata.name": projectName}.AsSelector().String(), + }) + if err != nil { + return errors.Wrapf(err, "unable to watch new project %s creation", projectName) + } + defer w.Stop() + + for { + val, ok := <-w.ResultChan() + if !ok { + break + } + if e, ok := val.Object.(*projectv1.Project); ok { + glog.V(4).Infof("Project %s now exists", e.Name) + return nil + } + } + } + return nil } diff --git a/pkg/occlient/occlient_test.go b/pkg/occlient/occlient_test.go index 22d12eed4..5a04ab897 100644 --- a/pkg/occlient/occlient_test.go +++ b/pkg/occlient/occlient_test.go @@ -2747,11 +2747,19 @@ func TestCreateNewProject(t *testing.T) { tests := []struct { name string projName string + wait bool wantErr bool }{ { - name: "Case 1: valid project name", + name: "Case 1: valid project name, not waiting", projName: "testing", + wait: false, + wantErr: false, + }, + { + name: "Case 2: valid project name, waiting", + projName: "testing2", + wait: true, wantErr: false, }, @@ -2767,6 +2775,24 @@ func TestCreateNewProject(t *testing.T) { t.Run(tt.name, func(t *testing.T) { fkclient, fkclientset := FakeNew() + if tt.wait { + fkWatch := watch.NewFake() + // Change the status + go func() { + fkWatch.Modify(&projectv1.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: tt.projName, + }, + }) + }() + fkclientset.ProjClientset.PrependWatchReactor("projects", func(action ktesting.Action) (handled bool, ret watch.Interface, err error) { + if len(tt.projName) == 0 { + return true, nil, fmt.Errorf("error watching project") + } + return true, fkWatch, nil + }) + } + fkclientset.ProjClientset.PrependReactor("create", "projectrequests", func(action ktesting.Action) (bool, runtime.Object, error) { proj := projectv1.Project{ ObjectMeta: metav1.ObjectMeta{ @@ -2776,21 +2802,35 @@ func TestCreateNewProject(t *testing.T) { return true, &proj, nil }) - err := fkclient.CreateNewProject(tt.projName) + err := fkclient.CreateNewProject(tt.projName, tt.wait) if !tt.wantErr == (err != nil) { t.Errorf("client.CreateNewProject(string) unexpected error %v, wantErr %v", err, tt.wantErr) } - if len(fkclientset.ProjClientset.Actions()) != 1 { - t.Errorf("expected 1 action in CreateNewProject got: %v", fkclientset.ProjClientset.Actions()) + actions := fkclientset.ProjClientset.Actions() + actionsNb := len(actions) + if !tt.wait && actionsNb != 1 { + t.Errorf("expected 1 action in CreateNewProject got: %v", actions) + } + if tt.wait && actionsNb != 2 { + t.Errorf("expected 2 actions in CreateNewProject when waiting for project creation got: %v", actions) } if err == nil { - createdProj := fkclientset.ProjClientset.Actions()[0].(ktesting.CreateAction).GetObject().(*projectv1.ProjectRequest) + createdProj := actions[0].(ktesting.CreateAction).GetObject().(*projectv1.ProjectRequest) if createdProj.Name != tt.projName { t.Errorf("project name does not match the expected name, expected: %s, got: %s", tt.projName, createdProj.Name) } + + if tt.wait { + expectedFields := fields.OneTermEqualSelector("metadata.name", tt.projName) + gotFields := actions[1].(ktesting.WatchAction).GetWatchRestrictions().Fields + + if !reflect.DeepEqual(expectedFields, gotFields) { + t.Errorf("Fields not matching: expected: %s, got %s", expectedFields, gotFields) + } + } } }) diff --git a/pkg/odo/cli/project/create.go b/pkg/odo/cli/project/create.go index fa60e5b34..afe01ad96 100644 --- a/pkg/odo/cli/project/create.go +++ b/pkg/odo/cli/project/create.go @@ -26,9 +26,9 @@ var ( // ProjectCreateOptions encapsulates the options for the odo project create command type ProjectCreateOptions struct { - // name of the project projectName string + wait bool // generic context options common to all commands *genericclioptions.Context @@ -53,10 +53,22 @@ func (pco *ProjectCreateOptions) Validate() (err error) { // Run runs the project create command func (pco *ProjectCreateOptions) Run() (err error) { - err = project.Create(pco.Client, pco.projectName) - if err != nil { - return err + if pco.wait { + s := log.Spinner("Waiting for project to come up") + err = project.Create(pco.Client, pco.projectName, true) + if err != nil { + return err + } else { + s.End(true) + log.Successf(`Project '%s' is ready for use`, pco.projectName) + } + } else { + err = project.Create(pco.Client, pco.projectName, false) + if err != nil { + return err + } } + err = project.SetCurrent(pco.Client, pco.projectName) if err != nil { return err @@ -80,5 +92,6 @@ func NewCmdProjectCreate(name, fullName string) *cobra.Command { }, } + projectCreateCmd.Flags().BoolVarP(&o.wait, "wait", "w", false, "Wait until the project is ready") return projectCreateCmd } diff --git a/pkg/project/project.go b/pkg/project/project.go index f34f27099..047b6607c 100644 --- a/pkg/project/project.go +++ b/pkg/project/project.go @@ -46,8 +46,8 @@ func SetCurrent(client *occlient.Client, projectName string) error { return nil } -func Create(client *occlient.Client, projectName string) error { - err := client.CreateNewProject(projectName) +func Create(client *occlient.Client, projectName string, wait bool) error { + err := client.CreateNewProject(projectName, wait) if err != nil { return errors.Wrap(err, "unable to create new project") }