configstore: implement projectgroup delete

This commit is contained in:
Simone Gotti 2019-05-13 00:23:57 +02:00
parent 5c15eb4db7
commit d551dcb820
6 changed files with 220 additions and 1 deletions

View File

@ -291,7 +291,7 @@ func (h *ActionHandler) DeleteProject(ctx context.Context, projectRef string) er
return err
}
// TODO(sgotti) delete project secrets/variables
// TODO(sgotti) implement childs garbage collection
actions := []*datamanager.Action{
{
ActionType: datamanager.ActionTypeDelete,

View File

@ -147,3 +147,47 @@ func (h *ActionHandler) CreateProjectGroup(ctx context.Context, projectGroup *ty
_, err = h.dm.WriteWal(ctx, actions, cgt)
return projectGroup, err
}
func (h *ActionHandler) DeleteProjectGroup(ctx context.Context, projectGroupRef string) error {
var projectGroup *types.ProjectGroup
var cgt *datamanager.ChangeGroupsUpdateToken
// must do all the checks in a single transaction to avoid concurrent changes
err := h.readDB.Do(func(tx *db.Tx) error {
var err error
// check project group existance
projectGroup, err = h.readDB.GetProjectGroup(tx, projectGroupRef)
if err != nil {
return err
}
if projectGroup == nil {
return util.NewErrBadRequest(errors.Errorf("project group %q doesn't exist", projectGroupRef))
}
// changegroup is the project group id.
cgNames := []string{util.EncodeSha256Hex(projectGroup.ID)}
cgt, err = h.readDB.GetChangeGroupsUpdateTokens(tx, cgNames)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// TODO(sgotti) implement childs garbage collection
actions := []*datamanager.Action{
{
ActionType: datamanager.ActionTypeDelete,
DataType: string(types.ConfigTypeProjectGroup),
ID: projectGroup.ID,
},
}
_, err = h.dm.WriteWal(ctx, actions, cgt)
return err
}

View File

@ -135,6 +135,10 @@ func (c *Client) CreateProjectGroup(ctx context.Context, projectGroup *types.Pro
return resProjectGroup, resp, err
}
func (c *Client) DeleteProjectGroup(ctx context.Context, projectGroupRef string) (*http.Response, error) {
return c.getResponse(ctx, "DELETE", fmt.Sprintf("/projectgroups/%s", url.PathEscape(projectGroupRef)), nil, jsonContent, nil)
}
func (c *Client) GetProject(ctx context.Context, projectRef string) (*Project, *http.Response, error) {
project := new(Project)
resp, err := c.getParsedResponse(ctx, "GET", fmt.Sprintf("/projects/%s", url.PathEscape(projectRef)), nil, jsonContent, nil, project)

View File

@ -243,3 +243,31 @@ func (h *CreateProjectGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
h.log.Errorf("err: %+v", err)
}
}
type DeleteProjectGroupHandler struct {
log *zap.SugaredLogger
ah *action.ActionHandler
}
func NewDeleteProjectGroupHandler(logger *zap.Logger, ah *action.ActionHandler) *DeleteProjectGroupHandler {
return &DeleteProjectGroupHandler{log: logger.Sugar(), ah: ah}
}
func (h *DeleteProjectGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
vars := mux.Vars(r)
projectGroupRef, err := url.PathUnescape(vars["projectgroupref"])
if err != nil {
httpError(w, util.NewErrBadRequest(err))
return
}
err = h.ah.DeleteProjectGroup(ctx, projectGroupRef)
if httpError(w, err) {
h.log.Errorf("err: %+v", err)
}
if err := httpResponse(w, http.StatusNoContent, nil); err != nil {
h.log.Errorf("err: %+v", err)
}
}

View File

@ -129,6 +129,7 @@ func (s *Configstore) Run(ctx context.Context) error {
projectGroupSubgroupsHandler := api.NewProjectGroupSubgroupsHandler(logger, s.ah, s.readDB)
projectGroupProjectsHandler := api.NewProjectGroupProjectsHandler(logger, s.ah, s.readDB)
createProjectGroupHandler := api.NewCreateProjectGroupHandler(logger, s.ah, s.readDB)
deleteProjectGroupHandler := api.NewDeleteProjectGroupHandler(logger, s.ah)
projectHandler := api.NewProjectHandler(logger, s.readDB)
createProjectHandler := api.NewCreateProjectHandler(logger, s.ah, s.readDB)
@ -178,6 +179,7 @@ func (s *Configstore) Run(ctx context.Context) error {
apirouter.Handle("/projectgroups/{projectgroupref}/subgroups", projectGroupSubgroupsHandler).Methods("GET")
apirouter.Handle("/projectgroups/{projectgroupref}/projects", projectGroupProjectsHandler).Methods("GET")
apirouter.Handle("/projectgroups", createProjectGroupHandler).Methods("POST")
apirouter.Handle("/projectgroups/{projectgroupref}", deleteProjectGroupHandler).Methods("DELETE")
apirouter.Handle("/projects/{projectref}", projectHandler).Methods("GET")
apirouter.Handle("/projects", createProjectHandler).Methods("POST")

View File

@ -507,6 +507,147 @@ func TestProjectGroupsAndProjects(t *testing.T) {
})
}
func TestProjectGroupDelete(t *testing.T) {
dir, err := ioutil.TempDir("", "agola")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
defer os.RemoveAll(dir)
ctx := context.Background()
cs, tetcd := setupConfigstore(t, ctx, dir)
defer shutdownEtcd(tetcd)
t.Logf("starting cs")
go func() {
if err := cs.Run(ctx); err != nil {
t.Fatalf("err: %v", err)
}
}()
// TODO(sgotti) change the sleep with a real check that all is ready
time.Sleep(2 * time.Second)
//user, err := cs.ah.CreateUser(ctx, &action.CreateUserRequest{UserName: "user01"})
//if err != nil {
// t.Fatalf("unexpected err: %v", err)
//}
org, err := cs.ah.CreateOrg(ctx, &types.Organization{Name: "org01"})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
// TODO(sgotti) change the sleep with a real check that user is in readdb
time.Sleep(2 * time.Second)
// create a projectgroup in org root project group
pg01, err := cs.ah.CreateProjectGroup(ctx, &types.ProjectGroup{Name: "projectgroup01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name)}, Visibility: types.VisibilityPublic})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
//create a child projectgroup in org root project group
spg01, err := cs.ah.CreateProjectGroup(ctx, &types.ProjectGroup{Name: "subprojectgroup01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: pg01.ID}, Visibility: types.VisibilityPublic})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
// create a project inside child projectgroup
project, err := cs.ah.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: spg01.ID}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
// create project secret
_, err = cs.ah.CreateSecret(ctx, &types.Secret{Name: "secret01", Parent: types.Parent{Type: types.ConfigTypeProject, ID: project.ID}, Type: types.SecretTypeInternal, Data: map[string]string{"secret01": "secretvar01"}})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
// create project variable
_, err = cs.ah.CreateVariable(ctx, &types.Variable{Name: "variable01", Parent: types.Parent{Type: types.ConfigTypeProject, ID: project.ID}, Values: []types.VariableValue{{SecretName: "secret01", SecretVar: "secretvar01"}}})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
// delete root projectgroup
if err = cs.ah.DeleteProjectGroup(ctx, pg01.ID); err != nil {
t.Fatalf("unexpected err: %v", err)
}
// recreate the same hierarchj using the paths
pg01, err = cs.ah.CreateProjectGroup(ctx, &types.ProjectGroup{Name: "projectgroup01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name)}, Visibility: types.VisibilityPublic})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
spg01, err = cs.ah.CreateProjectGroup(ctx, &types.ProjectGroup{Name: "subprojectgroup01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name, pg01.Name)}, Visibility: types.VisibilityPublic})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
project, err = cs.ah.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name, pg01.Name, spg01.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
secret, err := cs.ah.CreateSecret(ctx, &types.Secret{Name: "secret01", Parent: types.Parent{Type: types.ConfigTypeProject, ID: path.Join("org", org.Name, pg01.Name, spg01.Name, project.Name)}, Type: types.SecretTypeInternal, Data: map[string]string{"secret01": "secretvar01"}})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
variable, err := cs.ah.CreateVariable(ctx, &types.Variable{Name: "variable01", Parent: types.Parent{Type: types.ConfigTypeProject, ID: path.Join("org", org.Name, pg01.Name, spg01.Name, project.Name)}, Values: []types.VariableValue{{SecretName: "secret01", SecretVar: "secretvar01"}}})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
// Get by projectgroup id
projects, err := cs.ah.GetProjectGroupProjects(ctx, spg01.ID)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if diff := cmp.Diff(projects, []*types.Project{project}); diff != "" {
t.Error(diff)
}
// Get by projectgroup path
projects, err = cs.ah.GetProjectGroupProjects(ctx, path.Join("org", org.Name, pg01.Name, spg01.Name))
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if diff := cmp.Diff(projects, []*types.Project{project}); diff != "" {
t.Error(diff)
}
secrets, err := cs.ah.GetSecrets(ctx, types.ConfigTypeProject, project.ID, false)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if diff := cmp.Diff(secrets, []*types.Secret{secret}); diff != "" {
t.Error(diff)
}
secrets, err = cs.ah.GetSecrets(ctx, types.ConfigTypeProject, path.Join("org", org.Name, pg01.Name, spg01.Name, project.Name), false)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if diff := cmp.Diff(secrets, []*types.Secret{secret}); diff != "" {
t.Error(diff)
}
variables, err := cs.ah.GetVariables(ctx, types.ConfigTypeProject, project.ID, false)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if diff := cmp.Diff(variables, []*types.Variable{variable}); diff != "" {
t.Error(diff)
}
variables, err = cs.ah.GetVariables(ctx, types.ConfigTypeProject, path.Join("org", org.Name, pg01.Name, spg01.Name, project.Name), false)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if diff := cmp.Diff(variables, []*types.Variable{variable}); diff != "" {
t.Error(diff)
}
}
func TestOrgMembers(t *testing.T) {
dir, err := ioutil.TempDir("", "agola")
if err != nil {