From f73f0ba43444f975bdfadecbe0eeca9c050c7da0 Mon Sep 17 00:00:00 2001 From: Simone Gotti Date: Sat, 4 May 2019 15:16:49 +0200 Subject: [PATCH] gateway: move variable logic from api to actions --- internal/services/gateway/action/variable.go | 151 ++++++++++++++++++ internal/services/gateway/api/variable.go | 148 ++++------------- internal/services/gateway/common/variables.go | 2 +- .../services/gateway/common/variables_test.go | 4 +- internal/services/gateway/gateway.go | 6 +- internal/services/gateway/webhook.go | 2 +- 6 files changed, 193 insertions(+), 120 deletions(-) create mode 100644 internal/services/gateway/action/variable.go diff --git a/internal/services/gateway/action/variable.go b/internal/services/gateway/action/variable.go new file mode 100644 index 0000000..7c61c28 --- /dev/null +++ b/internal/services/gateway/action/variable.go @@ -0,0 +1,151 @@ +// Copyright 2019 Sorint.lab +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied +// See the License for the specific language governing permissions and +// limitations under the License. + +package action + +import ( + "context" + "net/http" + + "github.com/pkg/errors" + csapi "github.com/sorintlab/agola/internal/services/configstore/api" + "github.com/sorintlab/agola/internal/services/gateway/common" + "github.com/sorintlab/agola/internal/services/types" + "github.com/sorintlab/agola/internal/util" +) + +type GetVariablesRequest struct { + ParentType types.ConfigType + ParentRef string + + Tree bool + RemoveOverridden bool +} + +func (h *ActionHandler) GetVariables(ctx context.Context, req *GetVariablesRequest) ([]*csapi.Variable, []*csapi.Secret, error) { + var csvars []*csapi.Variable + var cssecrets []*csapi.Secret + + switch req.ParentType { + case types.ConfigTypeProjectGroup: + var err error + var resp *http.Response + csvars, resp, err = h.configstoreClient.GetProjectGroupVariables(ctx, req.ParentRef, req.Tree) + if err != nil { + return nil, nil, ErrFromRemote(resp, err) + } + cssecrets, resp, err = h.configstoreClient.GetProjectGroupSecrets(ctx, req.ParentRef, true) + if err != nil { + return nil, nil, ErrFromRemote(resp, err) + } + case types.ConfigTypeProject: + var err error + var resp *http.Response + csvars, resp, err = h.configstoreClient.GetProjectVariables(ctx, req.ParentRef, req.Tree) + if err != nil { + return nil, nil, ErrFromRemote(resp, err) + } + cssecrets, resp, err = h.configstoreClient.GetProjectSecrets(ctx, req.ParentRef, true) + if err != nil { + return nil, nil, ErrFromRemote(resp, err) + } + } + + if req.RemoveOverridden { + // remove overriden variables + csvars = common.FilterOverriddenVariables(csvars) + } + + return csvars, cssecrets, nil +} + +type CreateVariableRequest struct { + Name string + + ParentType types.ConfigType + ParentRef string + + Values []types.VariableValue +} + +func (h *ActionHandler) CreateVariable(ctx context.Context, req *CreateVariableRequest) (*csapi.Variable, []*csapi.Secret, error) { + if !util.ValidateName(req.Name) { + return nil, nil, util.NewErrBadRequest(errors.Errorf("invalid variable name %q", req.Name)) + } + + if len(req.Values) == 0 { + return nil, nil, util.NewErrBadRequest(errors.Errorf("empty variable values")) + } + + v := &types.Variable{ + Name: req.Name, + Parent: types.Parent{ + Type: req.ParentType, + ID: req.ParentRef, + }, + Values: req.Values, + } + + var cssecrets []*csapi.Secret + var rv *csapi.Variable + + switch req.ParentType { + case types.ConfigTypeProjectGroup: + var err error + var resp *http.Response + cssecrets, resp, err = h.configstoreClient.GetProjectGroupSecrets(ctx, req.ParentRef, true) + if err != nil { + return nil, nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get project group %q secrets", req.ParentRef)) + } + + h.log.Infof("creating project group variable") + rv, resp, err = h.configstoreClient.CreateProjectGroupVariable(ctx, req.ParentRef, v) + if err != nil { + return nil, nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to create variable")) + } + case types.ConfigTypeProject: + var err error + var resp *http.Response + cssecrets, resp, err = h.configstoreClient.GetProjectSecrets(ctx, req.ParentRef, true) + if err != nil { + return nil, nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get project %q secrets", req.ParentRef)) + } + + h.log.Infof("creating project variable") + rv, resp, err = h.configstoreClient.CreateProjectVariable(ctx, req.ParentRef, v) + if err != nil { + return nil, nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to create variable")) + } + } + h.log.Infof("variable %s created, ID: %s", rv.Name, rv.ID) + + return rv, cssecrets, nil +} + +func (h *ActionHandler) DeleteVariable(ctx context.Context, parentType types.ConfigType, parentRef, name string) error { + var err error + var resp *http.Response + switch parentType { + case types.ConfigTypeProjectGroup: + h.log.Infof("deleting project group variable") + resp, err = h.configstoreClient.DeleteProjectGroupVariable(ctx, parentRef, name) + case types.ConfigTypeProject: + h.log.Infof("deleting project variable") + resp, err = h.configstoreClient.DeleteProjectVariable(ctx, parentRef, name) + } + if err != nil { + return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete variable")) + } + return nil +} diff --git a/internal/services/gateway/api/variable.go b/internal/services/gateway/api/variable.go index 0d6a214..81b2041 100644 --- a/internal/services/gateway/api/variable.go +++ b/internal/services/gateway/api/variable.go @@ -19,13 +19,13 @@ import ( "net/http" csapi "github.com/sorintlab/agola/internal/services/configstore/api" + "github.com/sorintlab/agola/internal/services/gateway/action" "github.com/sorintlab/agola/internal/services/gateway/common" "github.com/sorintlab/agola/internal/services/types" "github.com/sorintlab/agola/internal/util" "go.uber.org/zap" "github.com/gorilla/mux" - "github.com/pkg/errors" ) type VariableValue struct { @@ -68,19 +68,19 @@ func createVariableResponse(v *csapi.Variable, secrets []*csapi.Secret) *Variabl } type VariableHandler struct { - log *zap.SugaredLogger - configstoreClient *csapi.Client + log *zap.SugaredLogger + ah *action.ActionHandler } -func NewVariableHandler(logger *zap.Logger, configstoreClient *csapi.Client) *VariableHandler { - return &VariableHandler{log: logger.Sugar(), configstoreClient: configstoreClient} +func NewVariableHandler(logger *zap.Logger, ah *action.ActionHandler) *VariableHandler { + return &VariableHandler{log: logger.Sugar(), ah: ah} } func (h *VariableHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() query := r.URL.Query() _, tree := query["tree"] - _, removeoverriden := query["removeoverriden"] + _, removeoverridden := query["removeoverridden"] parentType, parentRef, err := GetConfigTypeRef(r) if httpError(w, err) { @@ -88,41 +88,16 @@ func (h *VariableHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - var csvars []*csapi.Variable - var cssecrets []*csapi.Secret - - switch parentType { - case types.ConfigTypeProjectGroup: - var err error - var resp *http.Response - csvars, resp, err = h.configstoreClient.GetProjectGroupVariables(ctx, parentRef, tree) - if httpErrorFromRemote(w, resp, err) { - h.log.Errorf("err: %+v", err) - return - } - cssecrets, resp, err = h.configstoreClient.GetProjectGroupSecrets(ctx, parentRef, true) - if httpErrorFromRemote(w, resp, err) { - h.log.Errorf("err: %+v", err) - return - } - case types.ConfigTypeProject: - var err error - var resp *http.Response - csvars, resp, err = h.configstoreClient.GetProjectVariables(ctx, parentRef, tree) - if httpErrorFromRemote(w, resp, err) { - h.log.Errorf("err: %+v", err) - return - } - cssecrets, resp, err = h.configstoreClient.GetProjectSecrets(ctx, parentRef, true) - if httpErrorFromRemote(w, resp, err) { - h.log.Errorf("err: %+v", err) - return - } + areq := &action.GetVariablesRequest{ + ParentType: parentType, + ParentRef: parentRef, + Tree: tree, + RemoveOverridden: removeoverridden, } - - if removeoverriden { - // remove overriden variables - csvars = common.FilterOverridenVariables(csvars) + csvars, cssecrets, err := h.ah.GetVariables(ctx, areq) + if httpError(w, err) { + h.log.Errorf("err: %+v", err) + return } variables := make([]*VariableResponse, len(csvars)) @@ -142,12 +117,12 @@ type CreateVariableRequest struct { } type CreateVariableHandler struct { - log *zap.SugaredLogger - configstoreClient *csapi.Client + log *zap.SugaredLogger + ah *action.ActionHandler } -func NewCreateVariableHandler(logger *zap.Logger, configstoreClient *csapi.Client) *CreateVariableHandler { - return &CreateVariableHandler{log: logger.Sugar(), configstoreClient: configstoreClient} +func NewCreateVariableHandler(logger *zap.Logger, ah *action.ActionHandler) *CreateVariableHandler { + return &CreateVariableHandler{log: logger.Sugar(), ah: ah} } func (h *CreateVariableHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -164,76 +139,31 @@ func (h *CreateVariableHandler) ServeHTTP(w http.ResponseWriter, r *http.Request httpError(w, util.NewErrBadRequest(err)) return } - - if !util.ValidateName(req.Name) { - httpError(w, util.NewErrBadRequest(errors.Errorf("invalid secret name %q", req.Name))) + areq := &action.CreateVariableRequest{ + Name: req.Name, + ParentType: parentType, + ParentRef: parentRef, + Values: req.Values, + } + csvar, cssecrets, err := h.ah.CreateVariable(ctx, areq) + if httpError(w, err) { + h.log.Errorf("err: %+v", err) return } - if len(req.Values) == 0 { - httpError(w, util.NewErrBadRequest(errors.Errorf("empty variable values"))) - return - } - - v := &types.Variable{ - Name: req.Name, - Parent: types.Parent{ - Type: parentType, - ID: parentRef, - }, - Values: req.Values, - } - - var cssecrets []*csapi.Secret - var rv *csapi.Variable - - switch parentType { - case types.ConfigTypeProjectGroup: - var err error - var resp *http.Response - cssecrets, resp, err = h.configstoreClient.GetProjectGroupSecrets(ctx, parentRef, true) - if httpErrorFromRemote(w, resp, err) { - h.log.Errorf("err: %+v", err) - return - } - - h.log.Infof("creating project group variable") - rv, resp, err = h.configstoreClient.CreateProjectGroupVariable(ctx, parentRef, v) - if httpErrorFromRemote(w, resp, err) { - h.log.Errorf("err: %+v", err) - return - } - case types.ConfigTypeProject: - var err error - var resp *http.Response - cssecrets, resp, err = h.configstoreClient.GetProjectSecrets(ctx, parentRef, true) - if httpErrorFromRemote(w, resp, err) { - h.log.Errorf("err: %+v", err) - return - } - - h.log.Infof("creating project variable") - rv, resp, err = h.configstoreClient.CreateProjectVariable(ctx, parentRef, v) - if httpErrorFromRemote(w, resp, err) { - h.log.Errorf("err: %+v", err) - return - } - } - h.log.Infof("variable %s created, ID: %s", rv.Name, rv.ID) - - res := createVariableResponse(rv, cssecrets) + res := createVariableResponse(csvar, cssecrets) if err := httpResponse(w, http.StatusCreated, res); err != nil { h.log.Errorf("err: %+v", err) } } type DeleteVariableHandler struct { - log *zap.SugaredLogger - configstoreClient *csapi.Client + log *zap.SugaredLogger + ah *action.ActionHandler } -func NewDeleteVariableHandler(logger *zap.Logger, configstoreClient *csapi.Client) *DeleteVariableHandler { - return &DeleteVariableHandler{log: logger.Sugar(), configstoreClient: configstoreClient} +func NewDeleteVariableHandler(logger *zap.Logger, ah *action.ActionHandler) *DeleteVariableHandler { + return &DeleteVariableHandler{log: logger.Sugar(), ah: ah} } func (h *DeleteVariableHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -247,16 +177,8 @@ func (h *DeleteVariableHandler) ServeHTTP(w http.ResponseWriter, r *http.Request return } - var resp *http.Response - switch parentType { - case types.ConfigTypeProjectGroup: - h.log.Infof("deleting project group variable") - resp, err = h.configstoreClient.DeleteProjectGroupVariable(ctx, parentRef, variableName) - case types.ConfigTypeProject: - h.log.Infof("deleting project variable") - resp, err = h.configstoreClient.DeleteProjectVariable(ctx, parentRef, variableName) - } - if httpErrorFromRemote(w, resp, err) { + err = h.ah.DeleteVariable(ctx, parentType, parentRef, variableName) + if httpError(w, err) { h.log.Errorf("err: %+v", err) return } diff --git a/internal/services/gateway/common/variables.go b/internal/services/gateway/common/variables.go index d016652..6c0310a 100644 --- a/internal/services/gateway/common/variables.go +++ b/internal/services/gateway/common/variables.go @@ -20,7 +20,7 @@ import ( "github.com/sorintlab/agola/internal/util" ) -func FilterOverridenVariables(variables []*csapi.Variable) []*csapi.Variable { +func FilterOverriddenVariables(variables []*csapi.Variable) []*csapi.Variable { variablesMap := map[string]*csapi.Variable{} for _, v := range variables { if _, ok := variablesMap[v.Name]; !ok { diff --git a/internal/services/gateway/common/variables_test.go b/internal/services/gateway/common/variables_test.go index e762ad3..f5f9d68 100644 --- a/internal/services/gateway/common/variables_test.go +++ b/internal/services/gateway/common/variables_test.go @@ -22,7 +22,7 @@ import ( "github.com/sorintlab/agola/internal/services/types" ) -func TestFilterOverridenVariables(t *testing.T) { +func TestFilterOverriddenVariables(t *testing.T) { tests := []struct { name string variables []*csapi.Variable @@ -106,7 +106,7 @@ func TestFilterOverridenVariables(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - out := FilterOverridenVariables(tt.variables) + out := FilterOverriddenVariables(tt.variables) if diff := cmp.Diff(tt.out, out); diff != "" { t.Error(diff) diff --git a/internal/services/gateway/gateway.go b/internal/services/gateway/gateway.go index 859dc7b..49afc1f 100644 --- a/internal/services/gateway/gateway.go +++ b/internal/services/gateway/gateway.go @@ -162,9 +162,9 @@ func (g *Gateway) Run(ctx context.Context) error { createSecretHandler := api.NewCreateSecretHandler(logger, g.configstoreClient) deleteSecretHandler := api.NewDeleteSecretHandler(logger, g.configstoreClient) - variableHandler := api.NewVariableHandler(logger, g.configstoreClient) - createVariableHandler := api.NewCreateVariableHandler(logger, g.configstoreClient) - deleteVariableHandler := api.NewDeleteVariableHandler(logger, g.configstoreClient) + variableHandler := api.NewVariableHandler(logger, g.ah) + createVariableHandler := api.NewCreateVariableHandler(logger, g.ah) + deleteVariableHandler := api.NewDeleteVariableHandler(logger, g.ah) currentUserHandler := api.NewCurrentUserHandler(logger, g.configstoreClient) userHandler := api.NewUserHandler(logger, g.configstoreClient) diff --git a/internal/services/gateway/webhook.go b/internal/services/gateway/webhook.go index 4b08289..b86d4ba 100644 --- a/internal/services/gateway/webhook.go +++ b/internal/services/gateway/webhook.go @@ -198,7 +198,7 @@ func (h *webhooksHandler) handleWebhook(r *http.Request) (int, string, error) { h.log.Infof("pvars: %v", util.Dump(pvars)) // remove overriden variables - pvars = common.FilterOverridenVariables(pvars) + pvars = common.FilterOverriddenVariables(pvars) h.log.Infof("pvars: %v", util.Dump(pvars)) // get project secrets