pkg/client/gerrit/gerrit.go (271 lines of code) (raw):

package gerrit import ( "bytes" "encoding/json" "fmt" "net/http" "os" "path/filepath" "regexp" "strings" "github.com/pkg/errors" "gopkg.in/resty.v1" ctrl "sigs.k8s.io/controller-runtime" gerritApi "github.com/epam/edp-gerrit-operator/v2/api/v1" "github.com/epam/edp-gerrit-operator/v2/pkg/client/ssh" "github.com/epam/edp-gerrit-operator/v2/pkg/service/gerrit/spec" "github.com/epam/edp-gerrit-operator/v2/pkg/service/platform" ) const ( acceptHeader = "Accept" applicationJson = "application/json" path = "/bin/sh" minBodyLength = 5 containerFlag = "-c" ) var log = ctrl.Log.WithName("client_gerrit") type Client struct { instance *gerritApi.Gerrit //TODO: remove this resty *resty.Client sshClient ssh.SSHClientInterface } func NewClient(instance *gerritApi.Gerrit, restyClient *resty.Client, sshClient ssh.SSHClientInterface) Client { return Client{ instance: instance, resty: restyClient.SetHeaders(map[string]string{ acceptHeader: applicationJson, }), sshClient: sshClient, } } func (gc *Client) Resty() *resty.Client { return gc.resty } // InitNewRestClient performs initialization of Gerrit connection. func (gc *Client) InitNewRestClient(instance *gerritApi.Gerrit, url, user, password string) error { gc.resty = resty.SetHostURL(url).SetBasicAuth(user, password).SetDisableWarn(true) gc.instance = instance return nil } func (gc *Client) InitNewSshClient(userName string, privateKey []byte, host string, port int32) error { client, err := ssh.SshInit(userName, privateKey, host, port, log) if err != nil { return errors.Wrap(err, "err while initializing new ssh client") } gc.sshClient = &client return nil } // CheckCredentials checks whether provided creds are correct. func (gc *Client) CheckCredentials() (int, error) { resp, err := gc.resty.R(). SetHeader(acceptHeader, applicationJson). Get("config/server/summary") if err != nil { return 0, errors.Wrapf(err, "Unable to verify Gerrit credentials") } return resp.StatusCode(), nil } // CheckGroup checks gerrit group. func (gc *Client) CheckGroup(groupName string) (*int, error) { vLog := log.WithValues("group name", groupName) vLog.Info("checking group...") statusNotFound := http.StatusNotFound uuid, err := gc.getGroupUuid(groupName) if err != nil { return nil, errors.Wrapf(err, "Unable to get Gerrit group uuid") } if uuid == "" { vLog.Info("group wasn't found") return &statusNotFound, nil } resp, err := gc.resty.R(). SetHeader(acceptHeader, applicationJson). Get(fmt.Sprintf("groups/%v", uuid)) if err != nil { return nil, errors.Wrapf(err, "Unable to get Gerrit groups") } status := resp.StatusCode() return &status, nil } // GetUser checks gerrit user. func (gc *Client) GetUser(username string) (*int, error) { resp, err := gc.resty.R(). SetHeader(acceptHeader, applicationJson). Get(fmt.Sprintf("accounts/%v", username)) if err != nil { return nil, errors.Wrapf(err, "Unable to get Gerrit user") } status := resp.StatusCode() return &status, nil } func (*Client) InitAdminUser(instance *gerritApi.Gerrit, k8sService platform.PlatformService, gerritScriptsPath, podName, gerritAdminPublicKey string) (*gerritApi.Gerrit, error) { addInitialAdminUserScript, err := os.ReadFile(filepath.FromSlash(fmt.Sprintf("%v/add-initial-admin-user.sh", gerritScriptsPath))) if err != nil { return instance, errors.Wrapf(err, "Failed to read add-initial-admin-user.sh script") } _, _, err = k8sService.ExecInPod(instance.Namespace, podName, []string{path, containerFlag, "mkdir -p /tmp/scripts && touch /tmp/scripts/add-initial-admin-user.sh && chmod +x /tmp/scripts/add-initial-admin-user.sh"}) if err != nil { return instance, errors.Wrapf(err, "Failed to create add-initial-admin-user.sh script inside gerrit pod") } _, _, err = k8sService.ExecInPod(instance.Namespace, podName, []string{path, containerFlag, fmt.Sprintf("echo \"%v\" > /tmp/scripts/add-initial-admin-user.sh", string(addInitialAdminUserScript))}) if err != nil { return instance, errors.Wrapf(err, "Failed to add content to add-initial-admin-user.sh script inside gerrit pod") } _, _, err = k8sService.ExecInPod(instance.Namespace, podName, []string{path, containerFlag, fmt.Sprintf("sh /tmp/scripts/add-initial-admin-user.sh \"%v\"", gerritAdminPublicKey)}) if err != nil { return instance, errors.Wrapf(err, "Failed to execute add-initial-admin-user.sh script inside gerrit pod") } return instance, nil } func (gc *Client) ChangePassword(username, password string) error { cmd := &ssh.SSHCommand{ Path: fmt.Sprintf("gerrit set-account --http-password \"%v\" \"%v\"", password, username), Env: []string{}, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, } out, err := gc.sshClient.RunCommand(cmd) if err != nil { return errors.Wrapf(err, "Changing %v password failed. %v", username, bytes.NewBuffer(out).String()) } return nil } func (gc *Client) ReloadPlugin(plugin string) error { cmd := &ssh.SSHCommand{ Path: fmt.Sprintf("gerrit plugin reload \"%v\"", plugin), Env: []string{}, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, } _, err := gc.sshClient.RunCommand(cmd) if err != nil { return errors.Wrapf(err, "Reloading %v plugin failed", plugin) } return nil } func (gc *Client) CreateUser(username, password, fullName, publicKey string) error { log.Info("creating user", "name", username) userStatus, err := gc.GetUser(username) if err != nil { return errors.Wrapf(err, "Getting %v user failed", username) } if *userStatus == http.StatusNotFound { cmd := &ssh.SSHCommand{ Path: fmt.Sprintf("gerrit create-account --full-name \"%v\" --http-password \"%v\" --ssh-key \"%v\" \"%v\"", fullName, password, publicKey, username), Env: []string{}, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, } _, err = gc.sshClient.RunCommand(cmd) if err != nil { return errors.Wrapf(err, "Creating %v user failed", username) } return nil } return nil } func (gc *Client) AddUserToGroups(userName string, groupNames []string) error { for _, group := range groupNames { groupStatus, err := gc.CheckGroup(group) if err != nil { return err } if *groupStatus == http.StatusNotFound { log.Info(fmt.Sprintf("Group %v not found in Gerrit", group)) } else { cmd := &ssh.SSHCommand{ Path: fmt.Sprintf("gerrit set-members --add \"%v\" \"%v\"", userName, group), Env: []string{}, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, } _, err := gc.sshClient.RunCommand(cmd) if err != nil { return errors.Wrapf(err, "Failed to add user %v to group %v", userName, group) } } } return nil } func (gc *Client) getGroupUuid(groupName string) (string, error) { re := regexp.MustCompile(fmt.Sprintf(`%v\t[A-Za-z0-9_]{40}`, groupName)) cmd := &ssh.SSHCommand{ Path: "gerrit ls-groups -v", Env: []string{}, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, } out, err := gc.sshClient.RunCommand(cmd) if err != nil { return "", errors.Wrap(err, "Receiving Gerrit groups password failed") } groups := bytes.NewBuffer(out).String() group := re.FindStringSubmatch(groups) if group == nil { return "", nil } uuid := strings.Split(group[0], "\t")[1] return uuid, nil } func (gc *Client) InitAllProjects(instance *gerritApi.Gerrit, k8sService platform.PlatformService, gerritScriptsPath string, podName string, _ string, ) error { initAllProjectsScript, err := os.ReadFile(filepath.FromSlash(fmt.Sprintf("%v/init-all-projects.sh", gerritScriptsPath))) if err != nil { return errors.Wrapf(err, "Failed to read init-all-projects.sh script") } gerritConfig, err := os.ReadFile(filepath.FromSlash(fmt.Sprintf("%v/../gerrit.config", gerritScriptsPath))) if err != nil { return errors.Wrapf(err, "Failed to read init-all-projects.sh script") } ciToolsGroupUuid, err := gc.getGroupUuid(spec.GerritCIToolsGroupName) if err != nil { return errors.Wrapf(err, "Failed to get %v group ID", spec.GerritCIToolsGroupName) } projectBootstrappersGroupUuid, err := gc.getGroupUuid(spec.GerritProjectBootstrappersGroupName) if err != nil { return errors.Wrapf(err, "Failed to get %v group ID", spec.GerritCIToolsGroupName) } developersGroupUuid, err := gc.getGroupUuid(spec.GerritProjectDevelopersGroupName) if err != nil { return errors.Wrapf(err, "Failed to get %v group ID", spec.GerritCIToolsGroupName) } readOnlyGroupUuid, err := gc.getGroupUuid(spec.GerritReadOnlyGroupName) if err != nil { return errors.Wrapf(err, "Failed to get %s group ID", spec.GerritReadOnlyGroupName) } _, _, err = k8sService.ExecInPod(instance.Namespace, podName, []string{path, containerFlag, "mkdir -p /tmp/scripts && touch /tmp/scripts/init-all-projects.sh && chmod +x /tmp/scripts/init-all-projects.sh"}) if err != nil { return errors.Wrapf(err, "Failed to create init-all-projects.sh script inside gerrit pod") } _, _, err = k8sService.ExecInPod(instance.Namespace, podName, []string{path, containerFlag, fmt.Sprintf("echo \"%v\" > /tmp/scripts/init-all-projects.sh", string(initAllProjectsScript))}) if err != nil { return errors.Wrapf(err, "Failed to create init-all-projects.sh script inside gerrit pod") } _, _, err = k8sService.ExecInPod(instance.Namespace, podName, []string{ path, containerFlag, fmt.Sprintf("sh /tmp/scripts/init-all-projects.sh \"%v\" \"%v\" \"%v\" \"%v\" \"%v\"", string(gerritConfig), ciToolsGroupUuid, projectBootstrappersGroupUuid, developersGroupUuid, readOnlyGroupUuid), }) if err != nil { return errors.Wrapf(err, "Failed to execute init-all-projects.sh script inside gerrit pod") } return nil } func decodeGerritResponse(body string, v interface{}) error { if len(body) < minBodyLength { return errors.New("wrong gerrit body format") } // gerrit has prefix )]}' in all responses so we need to truncate it if err := json.Unmarshal([]byte(body[minBodyLength:]), v); err != nil { return errors.Wrap(err, "unable to decode gerrit response") } return nil }