diff --git a/clients/gitlab/client.go b/clients/gitlab/client.go index f0978cdea96a5dceb8fe3f3146494669ee3f29db..4c73f70727f7bc3378d2033f493c9078c8fe0b1f 100644 --- a/clients/gitlab/client.go +++ b/clients/gitlab/client.go @@ -37,9 +37,11 @@ package gitlab import ( "fmt" + "net/http" + "time" + "github.com/romana/rlog" gogitlab "github.com/xanzy/go-gitlab" - "net/http" ) const itemsPerPage = 50 @@ -83,7 +85,7 @@ func (r Client) GetUser(userID int) (*gogitlab.User, int, error) { return nil, 0, err } - rlog.Debug(fmt.Sprintf("Completed call to GetUser userID: %d", user.ID)) + rlog.Debugf("Completed call to GetUser userID: %d", user.ID) return user, res.StatusCode, nil } @@ -148,19 +150,46 @@ func (r Client) AddUser(user *gogitlab.User, password string) (*gogitlab.User, i } // DeleteUser - -func (r Client) DeleteUser(userID int) (int, error) { +func (r Client) DeleteUser(userID int, waitInterval int, waitCount int) (int, error) { + // waiting will be skilled if waitCount is 0 + // setting wait will use a loop to wait until resource has completed deletion res, err := r.client.Users.DeleteUser(userID) if err != nil { return res.StatusCode, err } - rlog.Debug(fmt.Sprintf("Completed call to DeleteUser. Status Code: %d", res.StatusCode)) + rlog.Debugf("DeleteUser status: %d", res.StatusCode) + // wait for resource to be deleted + if waitCount > 0 { + done := false + retryCount := waitCount + getCustomeAttributes := true + opts := gogitlab.GetUsersOptions{ + WithCustomAttributes: &getCustomeAttributes, + } + for !done && retryCount > 0 { + _, res, err = r.client.Users.GetUser(userID, opts) + if err != nil && res == nil { + rlog.Warnf("DeleteUser hard error: %v", err) + return 0, err + } + if res.StatusCode == http.StatusNotFound { + done = true + } + retryCount = retryCount - 1 + rlog.Debugf("DeleteUser wait status: %d", res.StatusCode) + time.Sleep((time.Duration(waitInterval) * time.Millisecond)) + } + } + rlog.Debugf("Completed call to DeleteUser status: %d", res.StatusCode) return res.StatusCode, nil } // DeleteUserByUsername - convenience method -func (r Client) DeleteUserByUsername(username string) (int, error) { +func (r Client) DeleteUserByUsername(username string, waitInterval int, waitCount int) (int, error) { + // waiting will be skilled if waitCount is 0 + // setting wait will use a loop to wait until resource has completed deletion // expect return status code of http.StatusNoContent 204 or http.StatusNotFound 404 - + logPrefix := "Completed call to DeleteUserByUsername." var opts = gogitlab.ListUsersOptions{Username: &username} users, lres, err := r.client.Users.ListUsers(&opts) if err != nil { @@ -168,16 +197,16 @@ func (r Client) DeleteUserByUsername(username string) (int, error) { } if len(users) == 0 { statusCode := http.StatusNotFound - rlog.Debug(fmt.Sprintf("Completed call to DeleteUser. Status Code: %d", statusCode)) + rlog.Debug(fmt.Sprintf("%s User Not Found. Status Code: %d", logPrefix, statusCode)) return statusCode, nil } - res, err := r.client.Users.DeleteUser(users[0].ID) + statusCode, err := r.DeleteUser(users[0].ID, waitInterval, waitCount) if err != nil { - return res.StatusCode, err + return 0, err } - rlog.Debug(fmt.Sprintf("Completed call to DeleteUser. Status Code: %d", res.StatusCode)) - return res.StatusCode, nil + rlog.Debug(fmt.Sprintf("%s Status Code: %d", logPrefix, statusCode)) + return statusCode, nil } // GetGroup - @@ -191,6 +220,56 @@ func (r Client) GetGroup(groupID int) (*gogitlab.Group, int, error) { return group, res.StatusCode, nil } +// GetGroupByFullPath - +func (r Client) GetGroupByFullPath(fullPath *string) (*gogitlab.Group, int, error) { + // slow. get all groups and check for exact match to fullPath + listOptions := gogitlab.ListOptions{ + Page: 1, + PerPage: itemsPerPage, + } + + // some valid values path, name + logPrefix := "Completed call to GetGroupByFullPath." + orderBy := "path" + opts := gogitlab.ListGroupsOptions{ + ListOptions: listOptions, + OrderBy: &orderBy, + } + + group := &gogitlab.Group{} + var more = true + var found = false + + // while more and not found + for more && !found { + groups, res, err := r.client.Groups.ListGroups(&opts) + if err != nil { + return nil, 0, err + } + for x := 0; x < len(groups) && !found; x++ { + rlog.Debugf("GetGroupByFullPath checking group %s", groups[x].FullPath) + if groups[x].FullPath == *fullPath { + found = true + rlog.Debugf("GetGroupByFullPath FOUND group %s", groups[x].FullPath) + group = groups[x] + } + } + + if res.NextPage > 0 { + opts.ListOptions.Page = opts.ListOptions.Page + 1 + } else { + more = false + } + + } + if !found { + rlog.Debug(fmt.Sprintf("%s NOT FOUND %s", logPrefix, *fullPath)) + return nil, http.StatusNotFound, nil + } + rlog.Debug(fmt.Sprintf("%s FOUND %s", logPrefix, group.FullPath)) + return group, http.StatusFound, nil +} + // GetGroups - func (r Client) GetGroups(search *string) ([]*gogitlab.Group, error) { listOptions := gogitlab.ListOptions{ @@ -253,11 +332,33 @@ func (r Client) AddGroup(group *gogitlab.Group, parentID *int) (*gogitlab.Group, } // DeleteGroup - -func (r Client) DeleteGroup(groupID int) (int, error) { +func (r Client) DeleteGroup(groupID int, waitInterval int, waitCount int) (int, error) { + // waiting will be skilled if waitCount is 0 + // setting wait will use a loop to wait until resource has completed deletion res, err := r.client.Groups.DeleteGroup(groupID) if err != nil { - return res.StatusCode, err + return 0, err } + rlog.Debugf("DeleteGroup status: %d", res.StatusCode) + // wait for resource to be deleted + if waitCount > 0 { + done := false + retryCount := waitCount + for !done && retryCount > 0 { + _, res, err = r.client.Groups.GetGroup(groupID) + if err != nil && res == nil { + rlog.Warnf("DeleteGroup hard error: %v", err) + return 0, err + } + if res.StatusCode == http.StatusNotFound { + done = true + } + retryCount = retryCount - 1 + rlog.Debugf("DeleteGroup wait status: %d", res.StatusCode) + time.Sleep((time.Duration(waitInterval) * time.Millisecond)) + } + } + rlog.Debugf("Completed call to DeleteGroup status: %d", res.StatusCode) return res.StatusCode, nil } @@ -269,7 +370,7 @@ func (r Client) GetProject(projectID int) (*gogitlab.Project, error) { return nil, err } - rlog.Debug(fmt.Sprintf("Completed call to GetProject. status Code: %d", res.StatusCode)) + rlog.Debugf("Completed call to GetProject. status Code: %d", res.StatusCode) return project, nil } @@ -309,7 +410,7 @@ func (r Client) GetProjects(search *string) ([]*gogitlab.Project, error) { more = false } } - rlog.Debug(fmt.Sprintf("Completed call to GetProjectss. Records returned: %d", len(projectList))) + rlog.Debugf("Completed call to GetProjectss. Records returned: %d", len(projectList)) return projectList, nil } @@ -333,3 +434,12 @@ func (r Client) AddProject(project *gogitlab.Project, groupID int) (*gogitlab.Pr } return newProject, res.StatusCode, nil } + +// DeleteProject - +func (r Client) DeleteProject(projectID int) (int, error) { + res, err := r.client.Projects.DeleteProject(projectID) + if err != nil { + return res.StatusCode, err + } + return res.StatusCode, nil +} diff --git a/clients/gitlab/client_test.go b/clients/gitlab/client_test.go index 5927ed35f5e0040b628bae3e777761f8388ca894..4361ff22dc77a1a4e80eccbb9adee1dfce244be3 100644 --- a/clients/gitlab/client_test.go +++ b/clients/gitlab/client_test.go @@ -282,12 +282,30 @@ func TestClient_DeleteUser(t *testing.T) { testUserID := 1 testUsername := "testusername" testUser := gogitlab.User{ID: testUserID, Username: testUsername} - testUserArray := []*gogitlab.User{} - testUserArray = append(testUserArray, &testUser) + + // httpmock.RegisterResponder("DELETE", + // `=~^https://test/api/v4/users.*`, + // httpmock.NewJsonResponderOrPanic(202, testUser), + // ) + + // mock to list out the articles + counter := 0 httpmock.RegisterResponder("DELETE", `=~^https://test/api/v4/users.*`, - httpmock.NewJsonResponderOrPanic(202, testUser), + func(req *http.Request) (*http.Response, error) { + return httpmock.NewJsonResponse(201, testUser) + }, + ) + httpmock.RegisterResponder("GET", + `=~^https://test/api/v4/users.*`, + func(req *http.Request) (*http.Response, error) { + counter = counter + 1 + if counter%4 == 0 { + return httpmock.NewJsonResponse(404, testUser) + } + return httpmock.NewJsonResponse(202, testUser) + }, ) // test objects @@ -315,7 +333,7 @@ func TestClient_DeleteUser(t *testing.T) { name: "DeleteUser 1", fields: fields{client: testGitlabClient, token: testToken, apiURL: testAPIUrl}, args: args{userID: testUserID}, - want: 202, + want: 404, wantErr: false, }, } @@ -326,7 +344,7 @@ func TestClient_DeleteUser(t *testing.T) { token: tt.fields.token, apiURL: tt.fields.apiURL, } - got, err := r.DeleteUser(tt.args.userID) + got, err := r.DeleteUser(tt.args.userID, 1000, 10) if (err != nil) != tt.wantErr { t.Errorf("Client.DeleteUser() error = %v, wantErr %v", err, tt.wantErr) return @@ -396,7 +414,7 @@ func TestClient_DeleteUserByUsername(t *testing.T) { token: tt.fields.token, apiURL: tt.fields.apiURL, } - got, err := r.DeleteUserByUsername(tt.args.username) + got, err := r.DeleteUserByUsername(tt.args.username, 0, 0) if (err != nil) != tt.wantErr { t.Errorf("Client.DeleteUserByUsername() error = %v, wantErr %v", err, tt.wantErr) return @@ -632,13 +650,34 @@ func TestClient_DeleteGroup(t *testing.T) { testGroup2 := gogitlab.Group{ID: 2, Name: "group spongebob"} testGroupArray = append(testGroupArray, &testGroup1, &testGroup2) + // httpmock.RegisterResponder("DELETE", + // `=~^https://test/api/v4/groups.*`, + // httpmock.NewJsonResponderOrPanic(202, testGroup1), + // ) + // httpmock.RegisterResponder("GET", + // `=~^https://test/api/v4/groups.*`, + // httpmock.NewJsonResponderOrPanic(200, testGroupArray), + // ) + var counter = 0 httpmock.RegisterResponder("DELETE", `=~^https://test/api/v4/groups.*`, - httpmock.NewJsonResponderOrPanic(202, testGroup1), + func(req *http.Request) (*http.Response, error) { + // counter = counter + 1 + // if counter%4 == 0 { + // return httpmock.NewJsonResponse(404, testGroup1) + // } + return httpmock.NewJsonResponse(201, testGroup1) + }, ) httpmock.RegisterResponder("GET", `=~^https://test/api/v4/groups.*`, - httpmock.NewJsonResponderOrPanic(200, testGroupArray), + func(req *http.Request) (*http.Response, error) { + counter = counter + 1 + if counter%4 == 0 { + return httpmock.NewJsonResponse(404, testGroup1) + } + return httpmock.NewJsonResponse(202, testGroup1) + }, ) // test objects @@ -667,7 +706,7 @@ func TestClient_DeleteGroup(t *testing.T) { name: "DeleteGroup 1", fields: fields{client: testGitlabClient, token: testToken, apiURL: testAPIUrl}, args: args{groupID: testGroupID}, - want: 202, + want: 404, wantErr: false, }, } @@ -678,7 +717,7 @@ func TestClient_DeleteGroup(t *testing.T) { token: tt.fields.token, apiURL: tt.fields.apiURL, } - got, err := r.DeleteGroup(tt.args.groupID) + got, err := r.DeleteGroup(tt.args.groupID, 1000, 10) if (err != nil) != tt.wantErr { t.Errorf("Client.DeleteGroup() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/integration-tests/gitlab/api/gitlab_api_adhoc_test.go b/integration-tests/gitlab/api/gitlab_api_adhoc_test.go index 0f27134d0265d4c5060930166acf9a9f4aa2647d..04cff12cdfa0d05bf3c7f4ed888501974ba7ee86 100644 --- a/integration-tests/gitlab/api/gitlab_api_adhoc_test.go +++ b/integration-tests/gitlab/api/gitlab_api_adhoc_test.go @@ -3,15 +3,19 @@ package integration import ( - "testing" "errors" + "fmt" + "net/http" "os" + "testing" + gitlab "valkyrie.dso.mil/valkyrie-api/clients/gitlab" + "github.com/romana/rlog" gogitlab "github.com/xanzy/go-gitlab" ) -// getClient_AdHoc - +// getClient_AdHoc - func getClient_AdHoc() (gitlab.Client, error) { gitlabAPIURL, ok := os.LookupEnv("GITLAB_API_URL") if !ok { @@ -21,7 +25,7 @@ func getClient_AdHoc() (gitlab.Client, error) { if !ok { return gitlab.Client{}, errors.New("env variable GITLAB_API_TOKEN undefinded") } - var client = gitlab.NewClient(gitlabAPIURL, gitlabAPIToken,nil) + var client = gitlab.NewClient(gitlabAPIURL, gitlabAPIToken, nil) return client, nil } func TestClient_AdHoc_getUsers_All(t *testing.T) { @@ -37,7 +41,7 @@ func TestClient_AdHoc_getUsers_All(t *testing.T) { return } t.Logf("GetUsers %d", len(got)) - for x:=0;x>>>>>>> delete group %d", statusCode) + + } - groupName := "adam root" - groupPath := "adam-root" groupObj := gogitlab.Group{Name: groupName, Path: groupPath} - group, statusCode, err := c.AddGroup(&groupObj, nil) + group, statusCode, err = c.AddGroup(&groupObj, nil) if err != nil { t.Errorf("AddGroup error = %v", err) return } t.Logf("AddGroup: %s %d", group.FullPath, statusCode) - groupName = "adam subgroup" - groupPath = "adam-subgroup" + groupName = "adam subgroup1" + groupPath = "adam-subgroup1" groupObj = gogitlab.Group{Name: groupName, Path: groupPath} - subgroup, statusCode, err := c.AddGroup(&groupObj, &group.ID) + subgroup1, statusCode, err := c.AddGroup(&groupObj, &group.ID) if err != nil { t.Errorf("AddGroup error = %v", err) return } - t.Logf("AddGroup: %s %d", subgroup.FullPath, statusCode) - } + t.Logf("AddGroup: %s %d", subgroup1.FullPath, statusCode) + groupName = "adam subgroup2" + groupPath = "adam-subgroup2" + groupObj = gogitlab.Group{Name: groupName, Path: groupPath} + subgroup2, statusCode, err := c.AddGroup(&groupObj, &group.ID) + if err != nil { + t.Errorf("AddGroup error = %v", err) + return + } + t.Logf("AddGroup: %s %d", subgroup2.FullPath, statusCode) + + for i := 1; i <= 125; i++ { + projectName := "test project " + fmt.Sprintf("%d", i) + + projectObj := gogitlab.Project{Name: projectName} + got, statusCode, err := c.AddProject(&projectObj, group.ID) + + if err != nil { + t.Errorf("Client.AddProject() error = %v %d", err, statusCode) + } else { + t.Logf("AddProject: [%s, %s, %d]", got.NameWithNamespace, got.HTTPURLToRepo, statusCode) + } + } + for i := 1; i <= 125; i++ { + projectName := "test project " + fmt.Sprintf("%d", i) + + projectObj := gogitlab.Project{Name: projectName} + got, statusCode, err := c.AddProject(&projectObj, subgroup1.ID) + + if err != nil { + t.Errorf("Client.AddProject() error = %v %d", err, statusCode) + } else { + t.Logf("AddProject: [%s, %s, %d]", got.NameWithNamespace, got.HTTPURLToRepo, statusCode) + } + } }) } - func TestClient_AdHoc_getProjects_ALL(t *testing.T) { t.Run("test", func(t *testing.T) { c, err := getClient_AdHoc() @@ -192,7 +232,7 @@ func TestClient_AdHoc_getProjects_ALL(t *testing.T) { return } t.Logf("GetProjects %d", len(got)) - for x:=0;x -RLOG_LOG_LEVEL = DEBUG +RLOG_LOG_LEVEL = WARN RLOG_LOG_STREAM = stdout # time format - use ! to prevent override by environment variable RLOG_TIME_FORMAT="2006/01/06 15:04:05.00" # RLOG_TIME_FORMAT= UnixDate # optional list caller information -RLOG_CALLER_INFO= 1 +RLOG_CALLER_INFO= 0 # RLOG_LOG_FILE = /var/log/myapp.log