app/registry/external_reg.go (260 lines of code) (raw):

package registry import ( "context" "fmt" "net/http" "net/url" "time" "github.com/gin-gonic/gin" "github.com/pkg/errors" "gopkg.in/yaml.v3" "ddm-admin-console/router" "ddm-admin-console/service/gerrit" ) const ( ValuesLocation = "deploy-templates/values.yaml" MRLabelTarget = "console/target" MRLabelSubTarget = "console/sub-target" MRLabelSourceBranch = "console/source-branch" MRLabelTargetBranch = "console/target-branch" MRLabelAction = "console/action" MRLabelActionBranchMerge = "branch-merge" mrAnnotationRegName = "ext-reg/name" MRAnnotationActions = "actions" mrAnnotationRegType = "ext-reg/type" externalSystemTypeExternal = "external-system" erValuesIndex = "nontrembita-external-registration" erStatusInactive = "inactive" erStatusFailed = "failed" erStatusActive = "active" erStatusDisabled = "disabled" mrTargetExternalReg = "external-reg" mrTargetEditRegistry = "edit-registry" mrTargetEditTrembita = "trembita-registry-update" mrSubTargetCreation = "creation" mrSubTargetDisable = "disable" mrSubTargetEnable = "enable" mrSubTargetDeletion = "deletion" MRLabelApprove = "console/approve" MRLabelApproveAuto = "auto" ) type MRExists string func (m MRExists) Error() string { return string(m) } type ExternalRegistration struct { Name string `yaml:"name"` Enabled bool `yaml:"enabled"` External bool `yaml:"external"` StatusRegistration string `yaml:"-"` KeyValue string `yaml:"-"` } func (e ExternalRegistration) TypeStr() string { if e.External { return "external-system" } return "internal-registry" } func (e ExternalRegistration) Inactive() bool { return e.Status() == "status-inactive" || e.Status() == "status-failed" } func (e ExternalRegistration) Status() string { s := e.StatusRegistration if s == "" { s = erStatusActive } if !e.Enabled { s = erStatusDisabled } return fmt.Sprintf("status-%s", s) } func (a *App) addExternalReg(ctx *gin.Context) (router.Response, error) { userCtx := router.ContextWithUserAccessToken(ctx) registryName := ctx.Param("name") er := ExternalRegistration{ Name: ctx.PostForm("reg-name"), External: ctx.PostForm("external-system-type") == externalSystemTypeExternal, Enabled: true, } values, err := a.prepareRegistryValues(userCtx, registryName, &er) if err != nil { return nil, errors.Wrap(err, "unable to prepare registry values") } if err := a.createErMergeRequest(userCtx, ctx, registryName, er.Name, values, mrSubTargetCreation); err != nil { if _, ok := err.(MRExists); !ok { return nil, errors.Wrap(err, "unable to create MR") } } return router.MakeRedirectResponse(http.StatusFound, fmt.Sprintf("/admin/registry/view/%s", registryName)), nil } func GetValuesFromGit(projectName, branch string, gerritService gerrit.ServiceInterface) (*Values, error) { content, err := gerritService.GetBranchContent(projectName, branch, url.PathEscape(ValuesLocation)) if err != nil { return nil, errors.Wrap(err, "unable to get values yaml") } valuesBytes := []byte(content) var valuesDict map[string]interface{} if err := yaml.Unmarshal(valuesBytes, &valuesDict); err != nil { return nil, errors.Wrap(err, "unable to decode values yaml") } if valuesDict == nil { valuesDict = make(map[string]interface{}) } var vals Values if err := yaml.Unmarshal(valuesBytes, &vals); err != nil { return nil, errors.Wrap(err, "unable to decode values yaml") } vals.OriginalYaml = valuesDict return &vals, nil } func decodeExternalRegsFromValues(valuesDict map[string]interface{}) ([]ExternalRegistration, error) { eRegs := make([]ExternalRegistration, 0) var err error externalReg, ok := valuesDict[erValuesIndex] if ok { eRegs, err = convertExternalRegFromInterface(externalReg) if err != nil { return nil, errors.Wrap(err, "unable to convert external regs") } } return eRegs, nil } func (a *App) prepareRegistryValues(ctx context.Context, registryName string, er *ExternalRegistration) (string, error) { values, err := GetValuesFromGit(registryName, MasterBranch, a.Gerrit) if err != nil { return "", errors.Wrap(err, "unable to get values from git") } eRegs, err := decodeExternalRegsFromValues(values.OriginalYaml) if err != nil { return "", errors.Wrap(err, "unable to decode external regs") } for _, _er := range eRegs { if er.Name == _er.Name && _er.External == er.External { return "", errors.New("external reg system already exists") } } eRegs = append(eRegs, ExternalRegistration{ Name: er.Name, Enabled: er.Enabled, External: er.External, }) values.OriginalYaml[erValuesIndex] = eRegs newValues, err := yaml.Marshal(values.OriginalYaml) if err != nil { return "", errors.Wrap(err, "unable to encode new values yaml") } return string(newValues), nil } func (a *App) createErMergeRequest(userCtx context.Context, ctx *gin.Context, registryName, erName, values, action string) error { mrs, err := a.Services.Gerrit.GetMergeRequestByProject(userCtx, registryName) if err != nil { return errors.Wrap(err, "unable to get MRs") } for _, mr := range mrs { if mr.Status.Value == gerrit.StatusNew { return MRExists("there is already open merge request(s) for this registry") } } if err := a.Services.Gerrit.CreateMergeRequestWithContents(userCtx, &gerrit.MergeRequest{ ProjectName: registryName, Name: fmt.Sprintf("ers-mr-%s-%s-%d", registryName, erName, time.Now().Unix()), AuthorEmail: ctx.GetString(router.UserEmailSessionKey), AuthorName: ctx.GetString(router.UserNameSessionKey), CommitMessage: fmt.Sprintf("update registry external reg systems"), TargetBranch: "master", Labels: map[string]string{ MRLabelTarget: mrTargetExternalReg, MRLabelSubTarget: action, }, Annotations: map[string]string{ mrAnnotationRegName: erName, mrAnnotationRegType: ctx.PostForm("external-system-type"), }, }, map[string]string{ ValuesLocation: values, }); err != nil { return errors.Wrap(err, "unable to create MR with new values") } return nil } func (a *App) disableExternalReg(ctx *gin.Context) (router.Response, error) { userCtx := router.ContextWithUserAccessToken(ctx) registryName := ctx.Param("name") systemName := ctx.PostForm("reg-name") if systemName == "" { return nil, errors.New("reg-name is required") } vals, err := GetValuesFromGit(registryName, MasterBranch, a.Gerrit) if err != nil { return nil, errors.Wrap(err, "unable to get values from git") } eRegs, err := decodeExternalRegsFromValues(vals.OriginalYaml) if err != nil { return nil, errors.Wrap(err, "unable to decode external regs") } found := false mrSubTarget := mrSubTargetDisable for i, v := range eRegs { if v.Name == systemName { eRegs[i].Enabled = !eRegs[i].Enabled found = true if eRegs[i].Enabled { mrSubTarget = mrSubTargetEnable } } } if !found { return nil, errors.New("reg-name not found") } vals.OriginalYaml[erValuesIndex] = eRegs newValues, err := yaml.Marshal(vals.OriginalYaml) if err != nil { return nil, errors.Wrap(err, "unable to encode new values yaml") } if err := a.createErMergeRequest(userCtx, ctx, registryName, systemName, string(newValues), mrSubTarget); err != nil { if _, ok := err.(MRExists); !ok { return nil, errors.Wrap(err, "unable to create MR") } } return router.MakeRedirectResponse(http.StatusFound, fmt.Sprintf("/admin/registry/view/%s", registryName)), nil } func (a *App) removeExternalReg(ctx *gin.Context) (router.Response, error) { userCtx := router.ContextWithUserAccessToken(ctx) registryName := ctx.Param("name") systemName := ctx.PostForm("reg-name") if systemName == "" { return nil, errors.New("reg-name is required") } vals, err := GetValuesFromGit(registryName, MasterBranch, a.Gerrit) if err != nil { return nil, errors.Wrap(err, "unable to get values from git") } eRegs, err := decodeExternalRegsFromValues(vals.OriginalYaml) if err != nil { return nil, errors.Wrap(err, "unable to decode external regs") } found := false for i, v := range eRegs { if v.Name == systemName { eRegs = append(eRegs[:i], eRegs[i+1:]...) found = true } } if !found { return nil, errors.New("reg-name not found") } vals.OriginalYaml[erValuesIndex] = eRegs newValues, err := yaml.Marshal(vals.OriginalYaml) if err != nil { return nil, errors.Wrap(err, "unable to encode new values yaml") } if err := a.createErMergeRequest(userCtx, ctx, registryName, systemName, string(newValues), mrSubTargetDeletion); err != nil { if _, ok := err.(MRExists); !ok { return nil, errors.Wrap(err, "unable to create MR") } } return router.MakeRedirectResponse(http.StatusFound, fmt.Sprintf("/admin/registry/view/%s", registryName)), nil }