pkg/service/gerrit/gerrit.go (668 lines of code) (raw):
package gerrit
import (
"context"
"fmt"
"net/http"
"path/filepath"
"reflect"
"strings"
"time"
"github.com/dchest/uniuri"
"github.com/google/uuid"
"github.com/pkg/errors"
v1 "k8s.io/api/apps/v1"
coreV1Api "k8s.io/api/core/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
keycloakApi "github.com/epam/edp-keycloak-operator/api/v1"
gerritApi "github.com/epam/edp-gerrit-operator/v2/api/v1"
gerritClient "github.com/epam/edp-gerrit-operator/v2/pkg/client/gerrit"
"github.com/epam/edp-gerrit-operator/v2/pkg/client/git"
"github.com/epam/edp-gerrit-operator/v2/pkg/service/gerrit/spec"
"github.com/epam/edp-gerrit-operator/v2/pkg/service/helpers"
"github.com/epam/edp-gerrit-operator/v2/pkg/service/platform"
platformHelper "github.com/epam/edp-gerrit-operator/v2/pkg/service/platform/helper"
)
var log = ctrl.Log.WithName("service_gerrit")
const (
user = "user"
password = "password"
rsaID = "id_rsa"
rsaIDFile = "id_rsa.pub"
admin = "-admin"
DeploymentRestartAnnotation = "kubectl.kubernetes.io/restartedAt"
)
// Interface expresses behavior of the Gerrit EDP Component.
type Interface interface {
IsDeploymentReady(instance *gerritApi.Gerrit) (bool, error)
Configure(instance *gerritApi.Gerrit) (*gerritApi.Gerrit, bool, error)
ExposeConfiguration(ctx context.Context, instance *gerritApi.Gerrit) (*gerritApi.Gerrit, error)
Integrate(ctx context.Context, instance *gerritApi.Gerrit) (*gerritApi.Gerrit, error)
GetGerritSSHUrl(instance *gerritApi.Gerrit) (string, error)
GetServicePort(instance *gerritApi.Gerrit) (int32, error)
GetRestClient(gerritInstance *gerritApi.Gerrit) (gerritClient.ClientInterface, error)
GetGitClient(ctx context.Context, child Child, workDir string) (*git.Client, error)
}
type UserNotFoundError string
func (e UserNotFoundError) Error() string {
return string(e)
}
func IsErrUserNotFound(err error) bool {
var notFoundError UserNotFoundError
return errors.As(err, ¬FoundError)
}
// ComponentService implements gerrit.Interface.
type ComponentService struct {
// Providing Gerrit EDP component implementation through the interface (platform abstract)
PlatformService platform.PlatformService
client client.Client
k8sScheme *runtime.Scheme
gerritClient gerritClient.ClientInterface
runningInClusterFunc func() bool
}
// NewComponentService returns a new instance of a gerrit.Service type.
func NewComponentService(ps platform.PlatformService, kc client.Client, ks *runtime.Scheme) Interface {
return ComponentService{
PlatformService: ps,
client: kc,
k8sScheme: ks,
runningInClusterFunc: platformHelper.RunningInCluster,
gerritClient: &gerritClient.Client{},
}
}
func (s ComponentService) runningInCluster() bool {
if s.runningInClusterFunc != nil {
return s.runningInClusterFunc()
}
return false
}
// IsDeploymentReady check if DC for Gerrit is ready.
func (s ComponentService) IsDeploymentReady(instance *gerritApi.Gerrit) (bool, error) {
isReady, err := s.PlatformService.IsDeploymentReady(instance)
if err != nil {
return false, fmt.Errorf("failed to check readiness of Gerrit deployment %q: %w", instance.Name, err)
}
return isReady, nil
}
// Configure contains logic related to self configuration of the Gerrit EDP Component.
func (s ComponentService) Configure(instance *gerritApi.Gerrit) (*gerritApi.Gerrit, bool, error) {
gerritUrl, err := s.GetGerritSSHUrl(instance)
if err != nil {
return instance, false, errors.Wrap(err, "unable to get Gerrit SSH URL")
}
executableFilePath, err := platformHelper.GetExecutableFilePath()
if err != nil {
return instance, false, errors.Wrap(err, "unable to get executable file path")
}
gerritScriptsPath := platformHelper.LocalScriptsRelativePath
if !s.runningInCluster() {
gerritScriptsPath = filepath.FromSlash(fmt.Sprintf("%v/../%v/%v", executableFilePath, platformHelper.LocalConfigsRelativePath, platformHelper.DefaultScriptsDirectory))
}
err = s.PlatformService.CreateSecret(
instance,
instance.Name+"-admin-password",
map[string][]byte{
user: []byte(spec.GerritDefaultAdminUser),
password: []byte(uniuri.New()),
},
map[string]string{},
)
if err != nil {
return instance, false, errors.Wrapf(err, "failed to create admin Secret %s for Gerrit", instance.Name+"-admin-password")
}
sshPortService, err := s.GetServicePort(instance)
if err != nil {
return instance, false, err
}
sshPort, err := s.PlatformService.GetDeploymentSSHPort(instance)
if err != nil {
return instance, false, fmt.Errorf("failed to get SSH port for Gerrit instance %q: %w", instance.Name, err)
}
service, err := s.PlatformService.GetService(instance.Namespace, instance.Name)
if err != nil {
return instance, false, errors.Wrap(err, "unable to get Gerrit service")
}
err = s.PlatformService.UpdateService(service, sshPortService)
if err != nil {
return instance, false, errors.Wrapf(err, "unable to update Gerrit service")
}
dUpdated, err := s.updateDeploymentConfigPort(sshPort, sshPortService, instance)
if err != nil {
return instance, false, err
}
if dUpdated {
return instance, true, nil
}
gerritApiUrl, err := s.getGerritRestApiUrl(instance)
if err != nil {
return instance, false, errors.Wrapf(err, "failed to get Gerrit REST API URL %v/%v", instance.Namespace, instance.Name)
}
gerritAdminPassword, err := s.getGerritAdminPassword(instance)
if err != nil {
return instance, false, errors.Wrapf(err, "failed to get Gerrit admin password from secret for %s/%s", instance.Namespace, instance.Name)
}
podList, err := s.PlatformService.GetPods(instance.Namespace, &metaV1.ListOptions{LabelSelector: "app=" + instance.Name})
if err != nil || len(podList.Items) != 1 {
return instance, false, errors.Wrapf(err, "unable to determine Gerrit pod name: %v", len(podList.Items))
}
_, gerritAdminPublicKey, err := s.createSSHKeyPairs(instance, instance.Name+admin)
if err != nil {
return instance, false, errors.Wrapf(err, "failed to create Gerrit admin SSH keypair %v/%v", instance.Namespace, instance.Name)
}
err = s.gerritClient.InitNewRestClient(instance, gerritApiUrl, spec.GerritDefaultAdminUser, gerritAdminPassword)
if err != nil {
return instance, false, errors.Wrapf(err, "failed to initialize Gerrit REST client for %v/%v", instance.Namespace, instance.Name)
}
status, err := s.gerritClient.CheckCredentials()
if (status != http.StatusUnauthorized && status >= http.StatusBadRequest && status <= http.StatusHTTPVersionNotSupported) || err != nil {
return instance, false, errors.Wrap(err, "failed to check credentials in Gerrit")
}
if status == http.StatusUnauthorized {
// apparently we are trying to retry with default password
err = s.gerritClient.InitNewRestClient(instance, gerritApiUrl, spec.GerritDefaultAdminUser, spec.GerritDefaultAdminPassword)
if err != nil {
return instance, false, errors.Wrapf(err, "Failed to initialize Gerrit REST client for %v/%v", instance.Namespace, instance.Name)
}
status, err = s.gerritClient.CheckCredentials()
if (status != http.StatusUnauthorized && status >= http.StatusBadRequest && status <= http.StatusHTTPVersionNotSupported) || err != nil {
return instance, false, errors.Wrap(err, "failed to check credentials in Gerrit")
}
if status == http.StatusUnauthorized {
var adminInstance *gerritApi.Gerrit
adminInstance, err = s.gerritClient.InitAdminUser(instance, s.PlatformService, gerritScriptsPath, podList.Items[0].Name,
string(gerritAdminPublicKey))
if err != nil {
return adminInstance, false, errors.Wrap(err, "failed to initialize Gerrit Admin User")
}
}
err = s.setGerritAdminUserPassword(instance, gerritUrl, gerritAdminPassword, gerritApiUrl, sshPortService)
if err != nil {
return instance, false, err
}
}
gerritSecretName := instance.Name + admin
gerritAdminSshKeys, err := s.PlatformService.GetSecret(instance.Namespace, instance.Name+admin)
if err != nil {
return instance, false, fmt.Errorf("failed to get secret %q for gerrit: %w", gerritSecretName, err)
}
podName := podList.Items[0].Name
command := []string{"/bin/sh", "-c", "chown -R gerrit2:gerrit2 /var/gerrit/review_site"}
_, _, err = s.PlatformService.ExecInPod(instance.Namespace, podName, command)
if err != nil {
return instance, false, fmt.Errorf("failed to exec command %q in a pod %q: %w", strings.Join(command, " "), podName, err)
}
err = s.gerritClient.InitNewSshClient(spec.GerritDefaultAdminUser, gerritAdminSshKeys[rsaID], gerritUrl, sshPortService)
if err != nil {
return instance, false, fmt.Errorf("failed to init ssh client for gerrit: %w", err)
}
ciToolsStatus, err := s.gerritClient.CheckGroup(spec.GerritCIToolsGroupName)
if err != nil {
return instance, false, fmt.Errorf("failed to check Gerrit group %q: %w", spec.GerritCIToolsGroupName, err)
}
projectBootstrappersStatus, err := s.gerritClient.CheckGroup(spec.GerritProjectBootstrappersGroupName)
if err != nil {
return instance, false, fmt.Errorf("failed to check Gerrit group %q: %w", spec.GerritProjectBootstrappersGroupName, err)
}
if *ciToolsStatus == http.StatusNotFound || *projectBootstrappersStatus == http.StatusNotFound {
err = s.gerritClient.InitNewSshClient(spec.GerritDefaultAdminUser, gerritAdminSshKeys[rsaID], gerritUrl, sshPortService)
if err != nil {
return instance, false, fmt.Errorf("failed to init ssh client for gerrit: %w", err)
}
const errTemplate = "failed to create Gerrit group %q: %w"
_, err = s.gerritClient.CreateGroup(spec.GerritCIToolsGroupName, spec.GerritCIToolsGroupDescription,
true)
if err != nil {
return instance, false, fmt.Errorf(errTemplate, spec.GerritCIToolsGroupName, err)
}
_, err = s.gerritClient.CreateGroup(spec.GerritProjectBootstrappersGroupName,
spec.GerritProjectBootstrappersGroupDescription, true)
if err != nil {
return instance, false, fmt.Errorf(errTemplate, spec.GerritProjectBootstrappersGroupName, err)
}
_, err = s.gerritClient.CreateGroup(spec.GerritProjectDevelopersGroupName,
spec.GerritProjectDevelopersGroupNameDescription, true)
if err != nil {
return instance, false, fmt.Errorf(errTemplate, spec.GerritProjectDevelopersGroupName, err)
}
_, err = s.gerritClient.CreateGroup(spec.GerritReadOnlyGroupName, "", true)
if err != nil {
return instance, false, fmt.Errorf(errTemplate, spec.GerritReadOnlyGroupName, err)
}
err = s.gerritClient.InitAllProjects(instance, s.PlatformService, gerritScriptsPath, podList.Items[0].Name,
string(gerritAdminPublicKey))
if err != nil {
return instance, false, errors.Wrapf(err, "failed to initialize Gerrit All-Projects project")
}
}
return instance, false, nil
}
// ExposeConfiguration describes integration points of the Gerrit EDP Component for the other Operators and Components.
func (s ComponentService) ExposeConfiguration(ctx context.Context, instance *gerritApi.Gerrit) (*gerritApi.Gerrit, error) {
vLog := log.WithValues("gerrit", instance.Name)
vLog.Info("start exposing configuration")
err := s.initRestClient(instance)
if err != nil {
return instance, errors.Wrapf(err, "Failed to init Gerrit REST client")
}
err = s.initSSHClient(instance)
if err != nil {
return instance, errors.Wrapf(err, "Failed to init Gerrit SSH client")
}
ciUserSecretName := formatSecretName(instance.Name, spec.GerritDefaultCiUserSecretPostfix)
ciUserSshSecretName := fmt.Sprintf("%s-ciuser%s", instance.Name, spec.SshKeyPostfix)
if err = s.PlatformService.CreateSecret(
instance,
ciUserSecretName,
map[string][]byte{
user: []byte(spec.GerritDefaultCiUserUser),
password: []byte(uniuri.New()),
},
map[string]string{},
); err != nil {
return instance, errors.Wrapf(err, "Failed to create ci user Secret %v for Gerrit", ciUserSecretName)
}
ciUserAnnotationKey := helpers.GenerateAnnotationKey(spec.EdpCiUserSuffix)
s.setAnnotation(instance, ciUserAnnotationKey, ciUserSecretName)
ciUserCredentials, err := s.PlatformService.GetSecretData(instance.Namespace, ciUserSecretName)
if err != nil {
return nil, errors.Wrapf(err, "Failed to get Secret %s for %s/%s", ciUserSecretName,
instance.Namespace, instance.Name)
}
privateKey, publicKey, err := helpers.GenerateKeyPairs()
if err != nil {
return instance, errors.Wrapf(err, "Unable to generate SSH key pairs for Gerrit")
}
err = s.PlatformService.CreateSecret(
instance,
ciUserSshSecretName,
map[string][]byte{
"username": []byte(spec.GerritDefaultCiUserUser),
rsaID: privateKey,
rsaIDFile: publicKey,
},
map[string]string{
"app.edp.epam.com/secret-type": "repository",
},
)
if err != nil {
return instance, errors.Wrapf(err, "Failed to create Secret with SSH key pairs for Gerrit")
}
ciUserSshKeyAnnotationKey := helpers.GenerateAnnotationKey(spec.EdpCiUSerSshKeySuffix)
s.setAnnotation(instance, ciUserSshKeyAnnotationKey, ciUserSshSecretName)
err = s.gerritClient.CreateUser(spec.GerritDefaultCiUserUser, string(ciUserCredentials[password]),
"EDP CI bot", string(publicKey))
if err != nil {
return instance, errors.Wrapf(err, "Failed to create ci user %v in Gerrit", spec.GerritDefaultCiUserUser)
}
userGroups := map[string][]string{
spec.GerritDefaultCiUserUser: {spec.GerritProjectBootstrappersGroupName, spec.GerritAdministratorsGroup},
}
for _, userValue := range reflect.ValueOf(userGroups).MapKeys() {
userStrValue := userValue.String()
err = s.gerritClient.AddUserToGroups(userStrValue, userGroups[userStrValue])
if err != nil {
return instance, errors.Wrapf(err, "Failed to add user %v to groups %v", userStrValue, userGroups[userStrValue])
}
}
err = s.exposeArgoCDConfiguration(ctx, instance)
if err != nil {
return nil, err
}
err = s.client.Update(ctx, instance)
if err != nil {
return nil, errors.Wrap(err, "couldn't update project")
}
if instance.Spec.KeycloakSpec.Enabled {
var secret uuid.UUID
secret, err = uuid.NewUUID()
if err != nil {
return instance, errors.Wrap(err, "Failed to generate secret for Gerrit in Keycloak")
}
identityServiceClientCredentials := map[string][]byte{
"client_id": []byte(instance.Name),
"clientSecret": []byte(secret.String()),
}
identityServiceSecretName := formatSecretName(instance.Name, spec.IdentityServiceCredentialsSecretPostfix)
err = s.PlatformService.CreateSecret(
instance,
identityServiceSecretName,
identityServiceClientCredentials,
map[string]string{},
)
if err != nil {
return instance, errors.Wrapf(err, fmt.Sprintf("Failed to create secret %v", identityServiceSecretName))
}
annotationKey := helpers.GenerateAnnotationKey(spec.IdentityServiceCredentialsSecretPostfix)
s.setAnnotation(instance, annotationKey, formatSecretName(instance.Name, spec.IdentityServiceCredentialsSecretPostfix))
err = s.client.Update(ctx, instance)
if err != nil {
return nil, errors.Wrap(err, "couldn't update annotations")
}
}
return instance, nil
}
// Integrate applies actions required for the integration with the other EDP Components.
func (s ComponentService) Integrate(ctx context.Context, instance *gerritApi.Gerrit) (*gerritApi.Gerrit, error) {
l := ctrl.LoggerFrom(ctx)
externalUrl, err := s.getExternalUrl(instance)
if err != nil {
return nil, errors.Wrapf(err, "Failed to get Route for %v/%v", instance.Namespace, instance.Name)
}
if instance.Spec.KeycloakSpec.Enabled {
l.Info("Keycloak integration enabled")
var keycloakClient *keycloakApi.KeycloakClient
keycloakClient, err = s.getKeycloakClient(ctx, instance)
if err != nil {
return instance, err
}
if keycloakClient == nil {
err = s.createKeycloakClient(ctx, instance, externalUrl)
if err != nil {
return instance, err
}
}
var keycloakEnvironmentValue []coreV1Api.EnvVar
keycloakEnvironmentValue, err = s.PlatformService.GenerateKeycloakSettings(instance)
if err != nil {
return instance, fmt.Errorf("failed to generate Keycloak %q env values: %w", instance.Name, err)
}
if err = s.PlatformService.PatchDeploymentEnv(instance, keycloakEnvironmentValue); err != nil {
return instance, errors.Wrap(err, "Failed to add identity service information")
}
} else {
l.Info("Keycloak integration not enabled. Restarting deployment.")
gerritDep := &v1.Deployment{}
if err = s.client.Get(ctx, types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, gerritDep); err != nil {
return nil, fmt.Errorf("failed to get deployment %q: %w", instance.Name, err)
}
old := gerritDep.DeepCopy()
if gerritDep.Spec.Template.Annotations == nil {
gerritDep.Spec.Template.Annotations = map[string]string{}
}
gerritDep.Spec.Template.Annotations[DeploymentRestartAnnotation] = time.Now().Format(time.RFC3339)
if err = s.client.Patch(ctx, gerritDep, client.MergeFrom(old)); err != nil {
return nil, fmt.Errorf("failed to patch deployment: %w", err)
}
}
return instance, nil
}
func (s ComponentService) getExternalUrl(instance *gerritApi.Gerrit) (string, error) {
if instance.Spec.ExternalURL != "" {
return instance.Spec.ExternalURL, nil
}
h, sc, err := s.PlatformService.GetExternalEndpoint(instance.Namespace, instance.Name)
if err != nil {
return "", errors.Wrapf(err, "Failed to get Route for %v/%v", instance.Namespace, instance.Name)
}
return fmt.Sprintf("%v://%v", sc, h), nil
}
func (s ComponentService) getKeycloakClient(ctx context.Context, instance *gerritApi.Gerrit) (*keycloakApi.KeycloakClient, error) {
k8sClient := &keycloakApi.KeycloakClient{}
err := s.client.Get(ctx, types.NamespacedName{
Name: instance.Name,
Namespace: instance.Namespace,
}, k8sClient)
if err != nil {
if k8sErrors.IsNotFound(err) {
return nil, nil
}
return nil, fmt.Errorf("failed to Get k8s KeycloakClient object %q: %w", instance.Name, err)
}
return k8sClient, nil
}
func (s ComponentService) createKeycloakClient(ctx context.Context, instance *gerritApi.Gerrit, externalUrl string) error {
keycloakClient := &keycloakApi.KeycloakClient{
TypeMeta: metaV1.TypeMeta{
Kind: "KeycloakClient",
},
ObjectMeta: metaV1.ObjectMeta{
Name: instance.Name,
Namespace: instance.Namespace,
},
Spec: keycloakApi.KeycloakClientSpec{
ClientId: instance.Name,
Public: true,
WebUrl: externalUrl,
AdvancedProtocolMappers: false,
RealmRoles: &[]keycloakApi.RealmRole{
{
Name: "gerrit-administrators",
Composite: "administrator",
},
{
Name: "gerrit-users",
Composite: "developer",
},
},
},
}
if instance.Spec.KeycloakSpec.Realm != "" {
keycloakClient.Spec.TargetRealm = instance.Spec.KeycloakSpec.Realm
}
err := s.client.Create(ctx, keycloakClient)
if err != nil {
return fmt.Errorf("failed to create k8s client for KeycloakClient: %w", err)
}
return nil
}
func (s ComponentService) GetRestClient(gerritInstance *gerritApi.Gerrit) (gerritClient.ClientInterface, error) {
if s.gerritClient.Resty() != nil {
return s.gerritClient, nil
}
if err := s.initRestClient(gerritInstance); err != nil {
return nil, errors.Wrap(err, "unable to init gerrit rest client")
}
return s.gerritClient, nil
}
func (s *ComponentService) initRestClient(instance *gerritApi.Gerrit) error {
vLog := log.WithValues("gerrit", instance.Name)
vLog.Info("init rest client")
gerritAdminPassword, err := s.getGerritAdminPassword(instance)
if err != nil {
return errors.Wrapf(err, "Failed to get Gerrit admin password from secret for %s/%s", instance.Namespace, instance.Name)
}
gerritApiUrl, err := s.getGerritRestApiUrl(instance)
if err != nil {
return errors.Wrapf(err, "Failed to get Gerrit REST API URL %v/%v", instance.Namespace, instance.Name)
}
err = s.gerritClient.InitNewRestClient(instance, gerritApiUrl, spec.GerritDefaultAdminUser, gerritAdminPassword)
if err != nil {
return errors.Wrapf(err, "Failed to initialize Gerrit REST client for %v/%v", instance.Namespace, instance.Name)
}
vLog.Info("rest client has been initialized.")
return nil
}
func (s *ComponentService) initSSHClient(instance *gerritApi.Gerrit) error {
vLog := log.WithValues("gerrit", instance.Name)
vLog.Info("init ssh client")
gerritUrl, err := s.GetGerritSSHUrl(instance)
if err != nil {
return err
}
sshPortService, err := s.GetServicePort(instance)
if err != nil {
return err
}
secretName := instance.Name + admin
gerritAdminSshKeys, err := s.PlatformService.GetSecret(instance.Namespace, secretName)
if err != nil {
return fmt.Errorf("failed to fetch secret %q: %w", secretName, err)
}
err = s.gerritClient.InitNewSshClient(spec.GerritDefaultAdminUser, gerritAdminSshKeys[rsaID], gerritUrl, sshPortService)
if err != nil {
return errors.Wrapf(err, "Failed to init Gerrit SSH client %v/%v", instance.Namespace, instance.Name)
}
vLog.Info("ssh client has been initialized.")
return nil
}
func (s ComponentService) getGerritRestApiUrl(instance *gerritApi.Gerrit) (string, error) {
if instance.Spec.RestAPIUrl != "" {
return instance.Spec.RestAPIUrl, nil
}
gerritApiUrl := fmt.Sprintf("http://%v.%v:%v/%v", instance.Name, instance.Namespace, spec.GerritPort,
instance.Spec.GetBasePath())
if !s.runningInCluster() {
h, sc, err := s.PlatformService.GetExternalEndpoint(instance.Namespace, instance.Name)
if err != nil {
return "", errors.Wrapf(err, "Failed to get external endpoint for %v/%v", instance.Namespace, instance.Name)
}
gerritApiUrl = fmt.Sprintf("%v://%v/%v", sc, h, instance.Spec.GetBasePath())
}
return gerritApiUrl, nil
}
func (s ComponentService) GetGerritSSHUrl(instance *gerritApi.Gerrit) (string, error) {
if instance.Spec.SSHUrl != "" {
return instance.Spec.SSHUrl, nil
}
gerritSSHUrl := fmt.Sprintf("%v.%v", instance.Name, instance.Namespace)
if !s.runningInCluster() {
h, _, err := s.PlatformService.GetExternalEndpoint(instance.Namespace, instance.Name)
if err != nil {
return "", errors.Wrapf(err, "Failed to get Service for %v/%v", instance.Namespace, instance.Name)
}
gerritSSHUrl = h
}
return gerritSSHUrl, nil
}
func (s ComponentService) getGerritAdminPassword(instance *gerritApi.Gerrit) (string, error) {
secretName := fmt.Sprintf("%s-admin-password", instance.Name)
gerritAdminCredentials, err := s.PlatformService.GetSecretData(instance.Namespace, secretName)
if err != nil {
return "", errors.Wrapf(err, "Failed to get Secret %v for %v/%v", secretName, instance.Namespace, instance.Name)
}
return string(gerritAdminCredentials[password]), nil
}
func (s ComponentService) createSSHKeyPairs(instance *gerritApi.Gerrit, secretName string) (privateKey, publicKey []byte, err error) {
secretData, err := s.PlatformService.GetSecretData(instance.Namespace, secretName)
if err != nil {
return nil, nil, errors.Wrapf(err, "Unable to get data from secret %v", secretName)
}
if secretData != nil {
return secretData[rsaID], secretData[rsaIDFile], nil
}
privateKey, publicKey, err = helpers.GenerateKeyPairs()
if err != nil {
return nil, nil, errors.Wrapf(err, "Unable to generate SSH key pairs for Gerrit")
}
if err := s.PlatformService.CreateSecret(
instance,
secretName,
map[string][]byte{
rsaID: privateKey,
rsaIDFile: publicKey,
},
map[string]string{},
); err != nil {
return nil, nil, errors.Wrapf(err, "Failed to create Secret with SSH key pairs for Gerrit")
}
return
}
func (s ComponentService) setGerritAdminUserPassword(
instance *gerritApi.Gerrit,
gerritUrl, gerritAdminPassword, gerritApiUrl string,
sshPortService int32,
) error {
gerritAdminSshKeys, err := s.PlatformService.GetSecret(instance.Namespace, instance.Name+admin)
if err != nil {
return errors.Wrapf(err, "Failed to get Gerrit admin secret for %s/%s", instance.Namespace, instance.Name)
}
err = s.gerritClient.InitNewSshClient(spec.GerritDefaultAdminUser, gerritAdminSshKeys[rsaID], gerritUrl, sshPortService)
if err != nil {
return errors.Wrapf(err, "Failed to initialize Gerrit SSH client for %s/%s", instance.Namespace, instance.Name)
}
err = s.gerritClient.ChangePassword("admin", gerritAdminPassword)
if err != nil {
return errors.Wrapf(err, "Failed to set Gerrit admin password for %s/%s", instance.Namespace, instance.Name)
}
err = s.gerritClient.InitNewRestClient(instance, gerritApiUrl, spec.GerritDefaultAdminUser, gerritAdminPassword)
if err != nil {
return errors.Wrapf(err, "Failed to initialize Gerrit REST client for %s/%s", instance.Namespace, instance.Name)
}
return nil
}
func (s ComponentService) GetServicePort(instance *gerritApi.Gerrit) (int32, error) {
service, err := s.PlatformService.GetService(instance.Namespace, instance.Name)
if err != nil {
return 0, fmt.Errorf("failed to fetch service %q: %w", instance.Name, err)
}
for _, port := range service.Spec.Ports {
if port.Name == spec.SSHPortName {
return port.NodePort, nil
}
}
return 0, errors.New("Unable to determine Gerrit ssh port")
}
func (s ComponentService) updateDeploymentConfigPort(sshPort, sshPortService int32, instance *gerritApi.Gerrit) (bool, error) {
if sshPort != sshPortService || sshPort == 0 {
newEnv := []coreV1Api.EnvVar{
{
Name: spec.SSHListnerEnvName,
Value: fmt.Sprintf("*:%d", sshPortService),
},
}
if err := s.PlatformService.PatchDeploymentEnv(instance, newEnv); err != nil {
return false, fmt.Errorf("failed to update %q env value: %w", spec.SSHListnerEnvName, err)
}
return true, nil
}
return false, nil
}
// setAnnotation add key:value to current resource annotation.
func (ComponentService) setAnnotation(instance *gerritApi.Gerrit, key, value string) {
if len(instance.Annotations) == 0 {
instance.ObjectMeta.Annotations = map[string]string{
key: value,
}
} else {
instance.ObjectMeta.Annotations[key] = value
}
}
func formatSecretName(name, postfix string) string {
return fmt.Sprintf("%s-%s", name, postfix)
}
// exposeArgoCDConfiguration creates argocd user in Gerrit and adds this user to the ReadOnly group.
// It generates login/password and ssh private/public and stores them in secrets.
func (s *ComponentService) exposeArgoCDConfiguration(_ context.Context, gerrit *gerritApi.Gerrit) error {
argoUserSecretName := formatSecretName(gerrit.Name, spec.GerritArgoUserSecretPostfix)
argoUserSecretData := map[string][]byte{
user: []byte(spec.GerritArgoUser),
password: []byte(uniuri.New()),
}
err := s.PlatformService.CreateSecret(
gerrit,
argoUserSecretName,
argoUserSecretData,
map[string]string{},
)
if err != nil {
return fmt.Errorf("failed to create secret %s: %w", argoUserSecretName, err)
}
argoUserAnnotationKey := helpers.GenerateAnnotationKey(spec.EdpArgoUserSuffix)
s.setAnnotation(gerrit, argoUserAnnotationKey, argoUserSecretName)
privateKey, publicKey, err := helpers.GenerateSSHED25519KeyPairs()
if err != nil {
return fmt.Errorf("unable to generate SSH key pairs for Gerrit ArgoCD user: %w", err)
}
argoUserSshSecretName := fmt.Sprintf("%s-argocd%s", gerrit.Name, spec.SshKeyPostfix)
err = s.PlatformService.CreateSecret(
gerrit,
argoUserSshSecretName,
map[string][]byte{
"username": []byte(spec.GerritArgoUser),
rsaID: privateKey,
rsaIDFile: publicKey,
},
map[string]string{},
)
if err != nil {
return fmt.Errorf("unable to create secret for Gerrit ArgoCD user: %w", err)
}
ciUserSshKeyAnnotationKey := helpers.GenerateAnnotationKey(spec.EdpArgoUserSshKeySuffix)
s.setAnnotation(gerrit, ciUserSshKeyAnnotationKey, argoUserSshSecretName)
err = s.gerritClient.CreateUser(
spec.GerritArgoUser,
string(argoUserSecretData[password]),
"argo cd user",
string(publicKey),
)
if err != nil {
return fmt.Errorf("unable to create ArgoCD user in Gerrit: %w", err)
}
err = s.gerritClient.AddUserToGroups(spec.GerritArgoUser, []string{spec.GerritReadOnlyGroupName})
if err != nil {
return fmt.Errorf("unable to add ArgoCD user to %s group in Gerrit: %w", spec.GerritReadOnlyGroupName, err)
}
return nil
}