service/gerrit/service.go (258 lines of code) (raw):

package gerrit import ( "context" "ddm-admin-console/service" "encoding/json" "fmt" "io" "net/http" "path/filepath" "time" goGerrit "github.com/andygrunwald/go-gerrit" "github.com/pkg/errors" "gopkg.in/resty.v1" coreV1Api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" pkgScheme "sigs.k8s.io/controller-runtime/pkg/scheme" ) const ( //ViewTimeFormat = "02.01.2006 15:04" ViewTimeFormat = "2006-01-02 15:04:05" StatusMerged = "MERGED" StatusNew = "NEW" StatusAbandoned = "ABANDONED" CurrentRevision = "current" ) type Service struct { Config service.UserConfig k8sClient client.Client scheme *runtime.Scheme restConfig *rest.Config apiClient *resty.Client goGerritClient *goGerrit.Client goGerritHTTPClient *http.Client } type MergeRequest struct { Name string ProjectName string TargetBranch string SourceBranch string CommitMessage string AuthorName string AuthorEmail string Labels map[string]string Annotations map[string]string AdditionalArguments []string } type MRConfigMapFile struct { Path string `json:"path"` Contents string `json:"contents"` } type Config struct { Namespace string RootGerritName string GerritAPIUrlTemplate string } func Make(s *runtime.Scheme, k8sConfig *rest.Config, config Config) (*Service, error) { builder := pkgScheme.Builder{GroupVersion: schema.GroupVersion{Group: "v2.edp.epam.com", Version: "v1alpha1"}} builder.Register(&Gerrit{}, &GerritList{}, &GerritProject{}, &GerritProjectList{}, &GerritMergeRequest{}, &GerritMergeRequestList{}) if err := builder.AddToScheme(s); err != nil { return nil, fmt.Errorf("error during builder add to scheme, %w", err) } cl, err := client.New(k8sConfig, client.Options{ Scheme: s, }) if err != nil { return nil, fmt.Errorf("unable to init k8s jenkins client, %w", err) } svc := Service{ k8sClient: cl, scheme: s, UserConfig: service.UserConfig{ RestConfig: k8sConfig, }, restConfig: k8sConfig, Config: config, } if err := svc.initRestyClient(); err != nil { return nil, fmt.Errorf("unable to init resty client, %w", err) } return &svc, nil } func (s *Service) GetProjects(ctx context.Context) ([]GerritProject, error) { var projectList GerritProjectList if err := s.k8sClient.List(ctx, &projectList); err != nil { return nil, fmt.Errorf("unable to list gerrit projects, %w", err) } return projectList.Items, nil } func (s *Service) GetProject(ctx context.Context, name string) (*GerritProject, error) { prjs, err := s.GetProjects(ctx) if err != nil { return nil, fmt.Errorf("unable to get projects, %w", err) } for _, prj := range prjs { if prj.Spec.Name == name { return &prj, nil } } return nil, service.ErrNotFound("unable to find gerrit project") } func (s *Service) CreateProject(ctx context.Context, name string) error { if err := s.k8sClient.Create(ctx, &GerritProject{ ObjectMeta: metav1.ObjectMeta{ Namespace: s.Namespace, Name: fmt.Sprintf("gerrit-%s", name), }, Spec: GerritProjectSpec{ Name: name, CreateEmptyCommit: false, Parent: "All-Projects", }, }); err != nil { return fmt.Errorf("unable to create gerrit project, %w", err) } return nil } func (s *Service) GetMergeRequests(ctx context.Context) ([]GerritMergeRequest, error) { var mrs GerritMergeRequestList if err := s.k8sClient.List(ctx, &mrs); err != nil { return nil, fmt.Errorf("unable to get merge requests, %w", err) } return mrs.Items, nil } func (s *Service) GetMergeRequest(ctx context.Context, name string) (*GerritMergeRequest, error) { var mr GerritMergeRequest if err := s.k8sClient.Get(ctx, types.NamespacedName{Name: name, Namespace: s.Namespace}, &mr); err != nil { return nil, fmt.Errorf("unable to get gerrit merge request, %w", err) } return &mr, nil } func (s *Service) GetChangeDetails(changeID string) (*goGerrit.ChangeInfo, error) { info, _, err := s.goGerritClient.Changes.GetChangeDetail(changeID, &goGerrit.ChangeOptions{AdditionalFields: []string{"ALL_REVISIONS"}}) if err != nil { return nil, fmt.Errorf("unable to get change, %w", err) } return info, nil } func (s *Service) GetProjectInfo(projectName string) (*goGerrit.ProjectInfo, error) { info, _, err := s.goGerritClient.Projects.GetProject(projectName) if err != nil { return nil, fmt.Errorf("unable to get project info, %w", err) } return info, nil } func (s *Service) GetMergeRequestByChangeID(ctx context.Context, changeID string) (*GerritMergeRequest, error) { var mrs GerritMergeRequestList if err := s.k8sClient.List(ctx, &mrs); err != nil { return nil, fmt.Errorf("unable to list gerrit merge requests, %w", err) } for _, mr := range mrs.Items { if mr.Status.ChangeID == changeID { return &mr, nil } } return nil, errors.New("unable to find MR by changed ID") } func (s *Service) UpdateMergeRequestStatus(ctx context.Context, mr *GerritMergeRequest) error { if err := s.k8sClient.Status().Update(ctx, mr); err != nil { return fmt.Errorf("unable to update mr status, %w", err) } return nil } func (s *Service) GetMergeRequestByProject(ctx context.Context, projectName string) ([]GerritMergeRequest, error) { var mrs GerritMergeRequestList if err := s.k8sClient.List(ctx, &mrs); err != nil { return nil, fmt.Errorf("unable to list gerrit merge requests, %w", err) } var result []GerritMergeRequest for _, mr := range mrs.Items { if mr.Spec.ProjectName == projectName { result = append(result, mr) } } return result, nil } func (s *Service) CreateMergeRequest(ctx context.Context, mr *MergeRequest) error { if err := s.k8sClient.Create(ctx, &GerritMergeRequest{ ObjectMeta: metav1.ObjectMeta{Namespace: s.Namespace, Name: mr.Name, Labels: mr.Labels, Annotations: mr.Annotations}, Spec: GerritMergeRequestSpec{ OwnerName: s.RootGerritName, ProjectName: mr.ProjectName, TargetBranch: mr.TargetBranch, SourceBranch: mr.SourceBranch, CommitMessage: mr.CommitMessage, AuthorEmail: mr.AuthorEmail, AuthorName: mr.AuthorName, AdditionalArguments: mr.AdditionalArguments, }, }); err != nil { return fmt.Errorf("unable to create merge request, %w", err) } return nil } func (s *Service) CreateMergeRequestWithContents(ctx context.Context, mr *MergeRequest, contents map[string]string) error { changesCMName := fmt.Sprintf("mr-%s-values-%d", mr.ProjectName, time.Now().Unix()) cmData := make(map[string]string) for filePath, content := range contents { bts, err := json.Marshal(MRConfigMapFile{Path: filePath, Contents: content}) if err != nil { return fmt.Errorf("unable to encode file, %w", err) } cmData[filepath.Base(filePath)] = string(bts) } mergeRequestConfigMap := coreV1Api.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: changesCMName, Namespace: s.Namespace}, Data: cmData, } if err := s.k8sClient.Create(ctx, &mergeRequestConfigMap); err != nil { return fmt.Errorf("unable to create changes config map, %w", err) } if err := s.k8sClient.Create(ctx, &GerritMergeRequest{ ObjectMeta: metav1.ObjectMeta{Namespace: s.Namespace, Name: mr.Name, Labels: mr.Labels, Annotations: mr.Annotations}, Spec: GerritMergeRequestSpec{ OwnerName: s.RootGerritName, ProjectName: mr.ProjectName, TargetBranch: mr.TargetBranch, CommitMessage: mr.CommitMessage, AuthorEmail: mr.AuthorEmail, AuthorName: mr.AuthorName, ChangesConfigMap: changesCMName, SourceBranch: "", }, }); err != nil { return fmt.Errorf("unable to create merge request, %w", err) } return nil } func (s *Service) ApproveAndSubmitChange(changeID, username, email string) error { if _, rsp, err := s.GoGerritClient().Changes.SetReview(changeID, CurrentRevision, &goGerrit.ReviewInput{ Message: fmt.Sprintf("Submitted by %s [%s]", username, email), Labels: map[string]string{ "Code-Review": "2", "Verified": "1", }, }); err != nil { if rsp != nil { body, _ := io.ReadAll(rsp.Body) return errors.Wrapf(err, "unable to review change, error: %s", string(body)) } return fmt.Errorf("unable to review change, %w", err) } if _, rsp, err := s.GoGerritClient().Changes.SubmitChange(changeID, &goGerrit.SubmitInput{}); err != nil { if rsp != nil { body, _ := io.ReadAll(rsp.Body) return errors.Wrapf(err, "unable to submit change, error: %s", string(body)) } return fmt.Errorf("unable to submit change, %w", err) } return nil }