project: add remote repository config type

RemoteRepositoryConfigType defines how a remote repository is configured and
managed. Currently only "remotesource" is supported.

In future other config types (like a fully manual config) could be supported.
This commit is contained in:
Simone Gotti 2019-05-03 12:21:44 +02:00
parent ea02eed2d9
commit 1f09eea949
4 changed files with 61 additions and 29 deletions

View File

@ -135,6 +135,9 @@ func (s *CommandHandler) CreateProject(ctx context.Context, project *types.Proje
if !types.IsValidVisibility(project.Visibility) {
return nil, util.NewErrBadRequest(errors.Errorf("invalid project visibility"))
}
if !types.IsValidRemoteRepositoryConfigType(project.RemoteRepositoryConfigType) {
return nil, util.NewErrBadRequest(errors.Errorf("invalid project remote repository config type %q", project.RemoteRepositoryConfigType))
}
var cgt *datamanager.ChangeGroupsUpdateToken
@ -181,17 +184,22 @@ func (s *CommandHandler) CreateProject(ctx context.Context, project *types.Proje
return util.NewErrBadRequest(errors.Errorf("project group with name %q, path %q already exists", pg.Name, pp))
}
// check that the linked account matches the remote source
user, err := s.readDB.GetUserByLinkedAccount(tx, project.LinkedAccountID)
if err != nil {
return errors.Wrapf(err, "failed to get user with linked account id %q", project.LinkedAccountID)
}
la, ok := user.LinkedAccounts[project.LinkedAccountID]
if !ok {
return util.NewErrBadRequest(errors.Errorf("linked account id %q for user %q doesn't exist", project.LinkedAccountID, user.Name))
}
if la.RemoteSourceID != project.RemoteSourceID {
return util.NewErrBadRequest(errors.Errorf("linked account id %q remote source %q different than project remote source %q", project.LinkedAccountID, la.RemoteSourceID, project.RemoteSourceID))
if project.RemoteRepositoryConfigType == types.RemoteRepositoryConfigTypeRemoteSource {
// check that the linked account matches the remote source
user, err := s.readDB.GetUserByLinkedAccount(tx, project.LinkedAccountID)
if err != nil {
return errors.Wrapf(err, "failed to get user with linked account id %q", project.LinkedAccountID)
}
if user == nil {
return util.NewErrBadRequest(errors.Errorf("user for linked account %q doesn't exist", project.LinkedAccountID))
}
la, ok := user.LinkedAccounts[project.LinkedAccountID]
if !ok {
return util.NewErrBadRequest(errors.Errorf("linked account id %q for user %q doesn't exist", project.LinkedAccountID, user.Name))
}
if la.RemoteSourceID != project.RemoteSourceID {
return util.NewErrBadRequest(errors.Errorf("linked account id %q remote source %q different than project remote source %q", project.LinkedAccountID, la.RemoteSourceID, project.RemoteSourceID))
}
}
return nil

View File

@ -394,13 +394,13 @@ func TestProjectGroupsAndProjects(t *testing.T) {
time.Sleep(2 * time.Second)
t.Run("create a project in user root project group", func(t *testing.T) {
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic})
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
})
t.Run("create a project in org root project group", func(t *testing.T) {
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name)}, Visibility: types.VisibilityPublic})
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
@ -418,13 +418,13 @@ func TestProjectGroupsAndProjects(t *testing.T) {
}
})
t.Run("create a project in user non root project group with same name as a root project", func(t *testing.T) {
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name, "projectgroup01")}, Visibility: types.VisibilityPublic})
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name, "projectgroup01")}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err != nil {
t.Fatalf("unexpected err: %+#v", err)
}
})
t.Run("create a project in org non root project group with same name as a root project", func(t *testing.T) {
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name, "projectgroup01")}, Visibility: types.VisibilityPublic})
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name, "projectgroup01")}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
@ -433,7 +433,7 @@ func TestProjectGroupsAndProjects(t *testing.T) {
t.Run("create duplicated project in user root project group", func(t *testing.T) {
projectName := "project01"
expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("user", user.Name, projectName))
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic})
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err)
}
@ -441,7 +441,7 @@ func TestProjectGroupsAndProjects(t *testing.T) {
t.Run("create duplicated project in org root project group", func(t *testing.T) {
projectName := "project01"
expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("org", org.Name, projectName))
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name)}, Visibility: types.VisibilityPublic})
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err)
}
@ -450,7 +450,7 @@ func TestProjectGroupsAndProjects(t *testing.T) {
t.Run("create duplicated project in user non root project group", func(t *testing.T) {
projectName := "project01"
expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("user", user.Name, "projectgroup01", projectName))
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name, "projectgroup01")}, Visibility: types.VisibilityPublic})
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name, "projectgroup01")}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err)
}
@ -458,7 +458,7 @@ func TestProjectGroupsAndProjects(t *testing.T) {
t.Run("create duplicated project in org non root project group", func(t *testing.T) {
projectName := "project01"
expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("org", org.Name, "projectgroup01", projectName))
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name, "projectgroup01")}, Visibility: types.VisibilityPublic})
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name, "projectgroup01")}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err)
}
@ -466,14 +466,14 @@ func TestProjectGroupsAndProjects(t *testing.T) {
t.Run("create project in unexistent project group", func(t *testing.T) {
expectedErr := `project group with id "unexistentid" doesn't exist`
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: "unexistentid"}, Visibility: types.VisibilityPublic})
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: "unexistentid"}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err)
}
})
t.Run("create project without parent id specified", func(t *testing.T) {
expectedErr := "project parent id required"
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Visibility: types.VisibilityPublic})
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err)
}
@ -488,7 +488,7 @@ func TestProjectGroupsAndProjects(t *testing.T) {
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go cs.ch.CreateProject(ctx, &types.Project{Name: "project02", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic})
go cs.ch.CreateProject(ctx, &types.Project{Name: "project02", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
wg.Done()
}
wg.Wait()

View File

@ -98,13 +98,14 @@ func (c *CommandHandler) CreateProject(ctx context.Context, req *CreateProjectRe
Type: types.ConfigTypeProjectGroup,
ID: parentID,
},
Visibility: req.Visibility,
RemoteSourceID: rs.ID,
LinkedAccountID: la.ID,
RepositoryID: repo.ID,
RepositoryPath: req.RepoPath,
SkipSSHHostKeyCheck: req.SkipSSHHostKeyCheck,
SSHPrivateKey: string(privateKey),
Visibility: req.Visibility,
RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeRemoteSource,
RemoteSourceID: rs.ID,
LinkedAccountID: la.ID,
RepositoryID: repo.ID,
RepositoryPath: req.RepoPath,
SkipSSHHostKeyCheck: req.SkipSSHHostKeyCheck,
SSHPrivateKey: string(privateKey),
}
c.log.Infof("creating project")

View File

@ -148,6 +148,27 @@ type LinkedAccount struct {
Oauth2AccessTokenExpiresAt time.Time `json:"oauth_2_access_token_expires_at,omitempty"`
}
// RemoteRepositoryConfigType defines how a remote repository is configured and
// managed. Currently only "remotesource" is supported.
// In future other config types (like a fully manual config) could be supported.
type RemoteRepositoryConfigType string
const (
// RemoteRepositoryConfigTypeManual is currently only used for tests and not available for direct usage
RemoteRepositoryConfigTypeManual RemoteRepositoryConfigType = "manual"
RemoteRepositoryConfigTypeRemoteSource RemoteRepositoryConfigType = "remotesource"
)
func IsValidRemoteRepositoryConfigType(t RemoteRepositoryConfigType) bool {
switch t {
case RemoteRepositoryConfigTypeManual:
case RemoteRepositoryConfigTypeRemoteSource:
default:
return false
}
return true
}
type Project struct {
// The type version. Increase when a breaking change is done. Usually not
// needed when adding fields.
@ -161,6 +182,8 @@ type Project struct {
Visibility Visibility `json:"visibility,omitempty"`
// Remote Repository fields
RemoteRepositoryConfigType RemoteRepositoryConfigType `json:"remote_repository_config_type,omitempty"`
RemoteSourceID string `json:"remote_source_id,omitempty"`
LinkedAccountID string `json:"linked_account_id,omitempty"`