app/registry/view.go (386 lines of code) (raw):
package registry
import (
"context"
"ddm-admin-console/router"
"ddm-admin-console/service/codebase"
edpcomponent "ddm-admin-console/service/edp_component"
"ddm-admin-console/service/gerrit"
"ddm-admin-console/service/jenkins"
"encoding/json"
"fmt"
"sort"
"strings"
"strconv"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
)
func (a *App) viewRegistry(ctx *gin.Context) (router.Response, error) {
userCtx := router.ContextWithUserAccessToken(ctx)
registryName := ctx.Param("name")
values, err := GetValuesFromGit(registryName, MasterBranch, a.Services.Gerrit)
if err != nil {
return nil, errors.Wrap(err, "unable to get values from git")
}
valuesJson, err := json.Marshal(values)
if err != nil {
return nil, errors.Wrap(err, "unable to encode values")
}
viewParams := gin.H{
"timezone": a.Config.Timezone,
"values": values,
"valuesJson": string(valuesJson),
}
mrs, err := a.Services.Gerrit.GetMergeRequestByProject(userCtx, registryName)
if err != nil {
return nil, fmt.Errorf("unable to get merge reqyests by project, %w", err)
}
cbService, err := a.Services.Codebase.ServiceForContext(userCtx)
if err != nil {
return nil, fmt.Errorf("unable to init service for user context, %w", err)
}
reg, err := cbService.Get(registryName)
if err != nil {
return nil, fmt.Errorf("unable to get registry by name: %s, %w", registryName, err)
}
hasUpdate, _, registryVersion, err := HasUpdate(userCtx, a.Services.Gerrit, reg, MRTargetRegistryVersionUpdate)
if err != nil {
return nil, fmt.Errorf("unable to check for updates, %w", err)
}
if is, redirect := isVersionRedirect(ctx, "/admin/registry/view", reg.Name, registryVersion); is {
return redirect, nil
}
viewParams["hasUpdate"] = hasUpdate
for _, f := range a.viewRegistryProcessFunctions(mrs) {
if err := f(userCtx, reg, values, viewParams); err != nil {
return nil, errors.Wrap(err, "error during view registry function")
}
}
templateArgs, err := json.Marshal(viewParams)
if err != nil {
return nil, errors.Wrap(err, "unable to encode template arguments")
}
return router.MakeHTMLResponse(200, "registry/view.html", gin.H{
"page": "registry",
"registryVersion": MajorVersion(registryVersion.Core().Original()),
"templateArgs": string(templateArgs),
}), nil
}
func (a *App) viewRegistryProcessFunctions(mrs []gerrit.GerritMergeRequest) []func(ctx context.Context,
reg *codebase.Codebase, values *Values, viewParams gin.H) error {
return []func(ctx context.Context, reg *codebase.Codebase, values *Values, viewParams gin.H) error{
a.viewRegistryAllowedToEdit,
a.viewRegistryGetRegistryAndBranches,
a.viewRegistryGetEDPComponents,
a.makeViewRegistryGetMergeRequests(mrs),
a.makeViewRegistryExternalRegistration(mrs),
a.makeViewRegistryPublicAPI(mrs),
a.viewDNSConfig,
a.viewCIDRConfig,
a.viewAdministratorsConfig,
a.viewUpdateTrembitaRegistries,
a.viewGetMasterJobStatus,
}
}
func (a *App) viewGetMasterJobStatus(ctx context.Context, reg *codebase.Codebase, _ *Values, viewParams gin.H) error {
status, _, err := a.Jenkins.GetJobStatus(ctx, fmt.Sprintf("%s/view/MASTER/job/MASTER-Build-%s", reg.Name, reg.Name))
if err != nil {
if strings.Contains(err.Error(), "404") {
viewParams["mrAvailable"] = true
return nil
}
return fmt.Errorf("unable to get job status, %w", err)
}
viewParams["mrAvailable"] = status == jenkins.StatusSuccess || status == jenkins.StatusNotBuild ||
status == jenkins.StatusAborted || status == jenkins.StatusFailure
return nil
}
func (a *App) viewUpdateTrembitaRegistries(_ context.Context, _ *codebase.Codebase, values *Values, viewParams gin.H) error {
mrs, ok := viewParams["mergeRequests"]
if !ok {
return nil
}
trembitaMrs := make(map[string]ExtendedMergeRequests)
extendedMRs := mrs.([]ExtendedMergeRequests)
for _, mr := range extendedMRs {
name, ok := mr.Labels[MRLabelTrembitaRegsitryName]
if ok {
trembitaMrs[name] = mr
}
}
for i, r := range values.Trembita.Registries {
_, ok := trembitaMrs[i]
if ok {
r.UserID = "fake"
values.Trembita.Registries[i] = r
}
}
return nil
}
func (a *App) makeViewRegistryExternalRegistration(mrs []gerrit.GerritMergeRequest) func(userCtx context.Context,
reg *codebase.Codebase, values *Values, viewParams gin.H) error {
return func(userCtx context.Context, reg *codebase.Codebase, values *Values, viewParams gin.H) error {
eRegs, mergeRequestsForER := make([]ExternalRegistration, 0), make(map[string]struct{})
for _, mr := range mrs {
if mr.Labels[MRLabelTarget] == "external-reg" && mr.Status.Value == gerrit.StatusNew {
eRegs = append(eRegs, ExternalRegistration{Name: mr.Annotations[mrAnnotationRegName], Enabled: true,
External: mr.Annotations[mrAnnotationRegType] == externalSystemTypeExternal, StatusRegistration: erStatusInactive})
mergeRequestsForER[mr.Annotations[mrAnnotationRegName]] = struct{}{}
} else if mr.Labels[MRLabelTarget] == "external-reg" && mr.Status.Value != gerrit.StatusMerged && mr.Status.Value != gerrit.StatusAbandoned {
eRegs = append(eRegs, ExternalRegistration{Name: mr.Annotations[mrAnnotationRegName], Enabled: true,
External: mr.Annotations[mrAnnotationRegType] == externalSystemTypeExternal, StatusRegistration: erStatusFailed})
mergeRequestsForER[mr.Annotations[mrAnnotationRegName]] = struct{}{}
}
}
//TODO: refactor to values struct
_eRegs, err := decodeExternalRegsFromValues(values.OriginalYaml)
if err != nil {
return errors.Wrap(err, "unable to decode external regs")
}
for _, _er := range _eRegs {
if _, ok := mergeRequestsForER[_er.Name]; !ok {
eRegs = append(eRegs, _er)
}
}
if err := a.loadKeysForExternalRegs(userCtx, reg.Name, eRegs); err != nil {
return errors.Wrap(err, "unable load keys for ext regs")
}
viewParams["externalRegs"] = eRegs
viewParams["values"] = values
if err := a.loadCodebasesForExternalRegistrations(reg.Name, eRegs, viewParams); err != nil {
return errors.Wrap(err, "unable to load codebases for external reg")
}
return nil
}
}
func (a *App) makeViewRegistryPublicAPI(mrs []gerrit.GerritMergeRequest) func(userCtx context.Context,
reg *codebase.Codebase, values *Values, viewParams gin.H) error {
return func(userCtx context.Context, reg *codebase.Codebase, values *Values, viewParams gin.H) error {
publicAPI, mergeRequestsForER := make([]PublicAPI, 0), make(map[string]struct{})
for _, mr := range mrs {
if mr.Labels[MRLabelTarget] == mrTargetPublicAPIReg {
_publicAPI, err := a.makeViewPublicAPIMR(mr, reg.Name)
if err != nil {
return fmt.Errorf("unable to get public api from MR, %w", err)
}
if _publicAPI.Name != "" {
publicAPI = append(publicAPI, _publicAPI)
mergeRequestsForER[_publicAPI.Name] = struct{}{}
}
}
}
for _, _publicAPI := range values.PublicApi {
if _, ok := mergeRequestsForER[_publicAPI.Name]; !ok {
publicAPI = append(publicAPI, _publicAPI)
}
}
viewParams["publicApi"] = publicAPI
viewParams["values"] = values
return nil
}
}
func (a *App) loadKeysForExternalRegs(ctx context.Context, registryName string, eRegs []ExternalRegistration) error {
for i, er := range eRegs {
if er.External && er.Enabled {
s, err := a.Services.K8S.GetSecretFromNamespace(ctx, fmt.Sprintf("keycloak-client-%s-secret", er.Name),
registryName)
if k8sErrors.IsNotFound(err) {
eRegs[i].StatusRegistration = erStatusInactive
continue
} else if err != nil {
return errors.Wrap(err, "unable to get er system key")
}
eRegs[i].KeyValue = string(s.Data["clientSecret"])
}
}
return nil
}
func (a *App) loadCodebasesForExternalRegistrations(registryName string, eRegs []ExternalRegistration, viewParams gin.H) error {
cbs, err := a.Services.Codebase.GetAllByType("registry")
if err != nil {
return errors.Wrap(err, "unable to get all registries")
}
var availableRegs []codebase.Codebase
for _, cb := range cbs {
skip := false
for _, er := range eRegs {
if er.Name == cb.Name && !er.External {
skip = true
break
}
}
if !skip && cb.Name != registryName && cb.Status.Available && cb.DeletionTimestamp.IsZero() && cb.StrStatus() != "failed" {
availableRegs = append(availableRegs, cb)
}
}
availableRegsJson, err := json.Marshal(availableRegs)
if err != nil {
return errors.Wrap(err, "unable to encode values")
}
viewParams["externalRegAvailableRegistriesJSON"] = string(availableRegsJson)
return nil
}
func convertExternalRegFromInterface(in interface{}) ([]ExternalRegistration, error) {
js, err := json.Marshal(in)
if err != nil {
return nil, errors.Wrap(err, "unable to encode interface to json")
}
var res []ExternalRegistration
if err := json.Unmarshal(js, &res); err != nil {
return nil, errors.Wrap(err, "unable to decode json")
}
return res, nil
}
func (a *App) viewAdministratorsConfig(_ context.Context, _ *codebase.Codebase, values *Values, viewParams gin.H) error {
viewParams["admins"] = values.Administrators //TODO: remove this
return nil
}
func (a *App) viewCIDRConfig(userCtx context.Context, reg *codebase.Codebase, values *Values, viewParams gin.H) error {
if values.Global.WhiteListIP.AdminRoutes != "" {
viewParams["adminCIDR"] = strings.Split(values.Global.WhiteListIP.AdminRoutes, " ")
}
if values.Global.WhiteListIP.CitizenPortal != "" {
viewParams["citizenCIDR"] = strings.Split(values.Global.WhiteListIP.CitizenPortal, " ")
}
if values.Global.WhiteListIP.OfficerPortal != "" {
viewParams["officerCIDR"] = strings.Split(values.Global.WhiteListIP.OfficerPortal, " ")
}
return nil
}
func (a *App) viewDNSConfig(_ context.Context, _ *codebase.Codebase, values *Values, viewParams gin.H) error {
//TODO: refactor to values struct
valuesDict := values.OriginalYaml
portals, ok := valuesDict["portals"]
if !ok {
return nil
}
portalsDict := portals.(map[string]interface{})
if _, ok := portalsDict["citizen"]; ok {
citizenDict := portalsDict["citizen"].(map[string]interface{})
if citizenCustomDNS, ok := citizenDict["customDns"]; ok {
viewParams["citizenPortalHost"] = citizenCustomDNS.(map[string]interface{})["host"].(string)
}
}
if _, ok := portalsDict["officer"]; ok {
officerDict := portalsDict["officer"].(map[string]interface{})
if officerCustomDNS, ok := officerDict["customDns"]; ok {
viewParams["officerPortalHost"] = officerCustomDNS.(map[string]interface{})["host"].(string)
}
}
return nil
}
func (a *App) makeViewRegistryGetMergeRequests(mrs []gerrit.GerritMergeRequest) func(userCtx context.Context,
reg *codebase.Codebase, _ *Values, viewParams gin.H) error {
return func(userCtx context.Context, reg *codebase.Codebase, _ *Values, viewParams gin.H) error {
sort.Sort(gerrit.SortByCreationDesc(mrs))
emrs := make([]ExtendedMergeRequests, 0, len(mrs))
for _, mr := range mrs {
if mr.Status.Value == gerrit.StatusNew {
viewParams["openMergeRequests"] = true
}
emrs = append(emrs, ExtendedMergeRequests{GerritMergeRequest: mr})
}
viewParams["mergeRequests"] = emrs
return nil
}
}
func (a *App) viewRegistryAllowedToEdit(userCtx context.Context, reg *codebase.Codebase, _ *Values, viewParams gin.H) error {
k8sService, err := a.Services.K8S.ServiceForContext(userCtx)
if err != nil {
return errors.Wrap(err, "unable to init service for user context")
}
allowed, err := a.Services.Codebase.CheckIsAllowedToUpdate(reg.Name, k8sService)
if err != nil {
return errors.Wrap(err, "unable to check codebase creation access")
}
viewParams["allowedToEdit"] = allowed
return nil
}
func (a *App) viewRegistryGetRegistryAndBranches(userCtx context.Context, reg *codebase.Codebase, _ *Values, viewParams gin.H) error {
cbService, err := a.Services.Codebase.ServiceForContext(userCtx)
if err != nil {
return errors.Wrap(err, "unable to init service for user context")
}
branches, err := cbService.GetBranchesByCodebase(userCtx, reg.Name)
if err != nil {
return errors.Wrap(err, "unable to get registry branches")
}
if err := a.loadBranchesStatuses(userCtx, branches); err != nil {
return errors.Wrap(err, "unable to load branch statuses")
}
reg.Branches = branches
viewParams["registry"] = reg
viewParams["branches"] = branches
viewParams["created"] = reg.FormattedCreatedAtTimezone(a.Config.Timezone)
return nil
}
func (a *App) loadBranchesStatuses(ctx context.Context, branches []codebase.CodebaseBranch) error {
for i, b := range branches {
branchName := strings.ToUpper(b.Spec.BranchName)
status, build, err := a.Jenkins.GetJobStatus(ctx, fmt.Sprintf("%s/view/%s/job/%s-Build-%s", b.Spec.CodebaseName,
branchName, branchName, b.Spec.CodebaseName))
if err != nil {
if strings.Contains(err.Error(), "404") {
continue
}
return errors.Wrap(err, "unable to get branch build status")
}
buildString := strconv.FormatInt(build, 10)
branches[i].Status.Value = status
branches[i].Status.Build = &buildString
}
return nil
}
func (a *App) viewRegistryGetEDPComponents(userCtx context.Context, reg *codebase.Codebase, _ *Values, viewParams gin.H) error {
jenkinsComponent, err := a.Services.EDPComponent.Get(userCtx, "jenkins")
if err != nil {
return errors.Wrap(err, "unable to get jenkins edp component")
}
gerritComponent, err := a.Services.EDPComponent.Get(userCtx, "gerrit")
if err != nil {
return errors.Wrap(err, "unable to get gerrit edp component")
}
categories, err := a.Services.EDPComponent.GetAllCategory(userCtx, reg.Name)
if err != nil {
return errors.Wrap(err, "unable to list namespaced edp components")
}
viewParams["jenkinsURL"] = jenkinsComponent.Spec.Url
viewParams["gerritURL"] = gerritComponent.Spec.Url
viewParams["platformOperationalComponents"] = categories[edpcomponent.PlatformOperationalZone]
viewParams["platformAdministrationComponents"] = categories[edpcomponent.PlatformAdministrationZone]
_, ok := categories[edpcomponent.RegistryOperationalZone]
if ok {
viewParams["registryOperationalComponents"] = categories[edpcomponent.RegistryOperationalZone]
viewParams["registryAdministrationComponents"] = categories[edpcomponent.RegistryAdministrationZone]
} else {
//TODO: remove this hotfix
if err := a.loadRegistryEDPCats(userCtx, reg.Name, viewParams); err != nil {
return fmt.Errorf("unable to load registry edp component")
}
}
return nil
}
func (a *App) loadRegistryEDPCats(ctx context.Context, registryName string, viewParams gin.H) error {
components, err := a.Services.EDPComponent.GetAllNamespace(ctx, registryName, true)
if err != nil {
return fmt.Errorf("unable to get edp components")
}
var (
registryOperationalComponents []edpcomponent.EDPComponentItem
registryAdministrationComponents []edpcomponent.EDPComponentItem
)
comMap := map[string]*[]edpcomponent.EDPComponentItem{
"gerrit": ®istryAdministrationComponents,
"jenkins": ®istryAdministrationComponents,
"business-process-administration-portal": ®istryAdministrationComponents,
"citizen-portal": ®istryOperationalComponents,
"officer-portal": ®istryOperationalComponents,
}
for _, cmp := range components {
if list, ok := comMap[cmp.Name]; ok {
item := edpcomponent.PrepareComponentItem(cmp)
item.Title = cmp.Name
*list = append(*list, item)
}
}
viewParams["registryOperationalComponents"] = registryOperationalComponents
viewParams["registryAdministrationComponents"] = registryAdministrationComponents
return nil
}