pkg/service/platform/openshift/openshift.go (187 lines of code) (raw):

package openshift import ( "context" "encoding/json" "fmt" "log" "os" "regexp" "strconv" appsV1client "github.com/openshift/client-go/apps/clientset/versioned/typed/apps/v1" authV1Client "github.com/openshift/client-go/authorization/clientset/versioned/typed/authorization/v1" projectV1Client "github.com/openshift/client-go/project/clientset/versioned/typed/project/v1" routeV1Client "github.com/openshift/client-go/route/clientset/versioned/typed/route/v1" securityV1Client "github.com/openshift/client-go/security/clientset/versioned/typed/security/v1" templateV1Client "github.com/openshift/client-go/template/clientset/versioned/typed/template/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" "k8s.io/client-go/rest" gerritApi "github.com/epam/edp-gerrit-operator/v2/api/v1" "github.com/epam/edp-gerrit-operator/v2/pkg/service/gerrit/spec" "github.com/epam/edp-gerrit-operator/v2/pkg/service/helpers" platformHelper "github.com/epam/edp-gerrit-operator/v2/pkg/service/platform/helper" "github.com/epam/edp-gerrit-operator/v2/pkg/service/platform/k8s" ) // OpenshiftService implements platform.Service interface (OpenShift platform integration). type OpenshiftService struct { k8s.K8SService authClient *authV1Client.AuthorizationV1Client templateClient *templateV1Client.TemplateV1Client projectClient *projectV1Client.ProjectV1Client securityClient *securityV1Client.SecurityV1Client appClient *appsV1client.AppsV1Client routeClient routeV1Client.RouteV1Interface } const ( deploymentTypeEnvName = "DEPLOYMENT_TYPE" deploymentConfigsDeploymentType = "deploymentConfigs" ) // Init process with OpenshiftService instance initialization actions. func (s *OpenshiftService) Init(config *rest.Config, scheme *runtime.Scheme) error { if err := s.K8SService.Init(config, scheme); err != nil { helpers.LogError(err) return fmt.Errorf("failed to init k8s client: %w", err) } templateClient, err := templateV1Client.NewForConfig(config) if err != nil { helpers.LogError(err) return fmt.Errorf("failed to create TemplateV1Client: %w", err) } s.templateClient = templateClient projectClient, err := projectV1Client.NewForConfig(config) if err != nil { helpers.LogError(err) return fmt.Errorf("failed to create ProjectV1Client: %w", err) } s.projectClient = projectClient securityClient, err := securityV1Client.NewForConfig(config) if err != nil { helpers.LogError(err) return fmt.Errorf("failed to create SecurityV1Client: %w", err) } s.securityClient = securityClient appClient, err := appsV1client.NewForConfig(config) if err != nil { helpers.LogError(err) return fmt.Errorf("failed to create AppsV1Client: %w", err) } s.appClient = appClient routeClient, err := routeV1Client.NewForConfig(config) if err != nil { helpers.LogError(err) return fmt.Errorf("failed to create RouteV1Client: %w", err) } s.routeClient = routeClient authClient, err := authV1Client.NewForConfig(config) if err != nil { helpers.LogError(err) return fmt.Errorf("failed to create AuthorizationV1Client: %w", err) } s.authClient = authClient return nil } // GetExternalEndpoint returns host and scheme associated with the object. func (s *OpenshiftService) GetExternalEndpoint(namespace, name string) (string, string, error) { ctx := context.Background() route, err := s.routeClient.Routes(namespace).Get(ctx, name, metaV1.GetOptions{}) if err != nil { if k8sErrors.IsNotFound(err) { log.Printf("Route %v in namespace %v not found", name, namespace) return "", "", fmt.Errorf("failed to find route %q in namespace %q: %w", name, namespace, err) } return "", "", fmt.Errorf("failed to Get OpenShift route %q: %w", name, err) } host := route.Spec.Host scheme := platformHelper.RouteHTTPScheme if route.Spec.TLS.Termination != "" { scheme = platformHelper.RouteHTTPSScheme } return host, scheme, nil } func (s *OpenshiftService) GetDeploymentSSHPort(instance *gerritApi.Gerrit) (int32, error) { ctx := context.Background() if os.Getenv(deploymentTypeEnvName) == deploymentConfigsDeploymentType { dc, err := s.appClient.DeploymentConfigs(instance.Namespace).Get(ctx, instance.Name, metaV1.GetOptions{}) if err != nil { return 0, fmt.Errorf("failed to GET OpenShift Deployment %q: %w", instance.Name, err) } for _, env := range dc.Spec.Template.Spec.Containers[0].Env { if env.Name == spec.SSHListnerEnvName { p, err := getPort(env.Value) if err != nil { return 0, err } return p, nil } } return 0, nil } p, err := s.K8SService.GetDeploymentSSHPort(instance) if err != nil { return 0, fmt.Errorf("failed to Get gerrit ssh port in k8s deployment: %w", err) } return p, nil } func (s *OpenshiftService) IsDeploymentReady(instance *gerritApi.Gerrit) (bool, error) { ctx := context.Background() if os.Getenv(deploymentTypeEnvName) == deploymentConfigsDeploymentType { dc, err := s.appClient.DeploymentConfigs(instance.Namespace).Get(ctx, instance.Name, metaV1.GetOptions{}) if err != nil { return false, fmt.Errorf("failed to Get Gerrit Deployment Config %q: %w", instance.Name, err) } return dc.Status.UpdatedReplicas == 1 && dc.Status.AvailableReplicas == 1, nil } ready, err := s.K8SService.IsDeploymentReady(instance) if err != nil { return false, fmt.Errorf("failed to check if deployment %q is ready: %w", instance.Name, err) } return ready, nil } func (s *OpenshiftService) PatchDeploymentEnv(gerrit *gerritApi.Gerrit, env []coreV1Api.EnvVar) error { ctx := context.Background() if os.Getenv(deploymentTypeEnvName) == deploymentConfigsDeploymentType { dc, err := s.appClient.DeploymentConfigs(gerrit.Namespace).Get(ctx, gerrit.Name, metaV1.GetOptions{}) if err != nil { return fmt.Errorf("failed to Get Gerrit Deployment Config %q: %w", gerrit.Name, err) } if len(env) == 0 { return nil } container, err := platformHelper.SelectContainer(dc.Spec.Template.Spec.Containers, gerrit.Name) if err != nil { return fmt.Errorf("didn't found container %q: %w", gerrit.Name, err) } container.Env = platformHelper.UpdateEnv(container.Env, env) dc.Spec.Template.Spec.Containers = append(dc.Spec.Template.Spec.Containers, container) jsonDc, err := json.Marshal(dc) if err != nil { return err } _, err = s.appClient.DeploymentConfigs(dc.Namespace).Patch(ctx, dc.Name, types.StrategicMergePatchType, jsonDc, metaV1.PatchOptions{}) if err != nil { return fmt.Errorf("failed to patch OpenShift Deployment Config %q: %w", dc.Name, err) } return nil } err := s.K8SService.PatchDeploymentEnv(gerrit, env) if err != nil { return fmt.Errorf("fail to update k8s deployment env: %w", err) } return nil } func getPort(value string) (int32, error) { re := regexp.MustCompile(`\d+`) if re.MatchString(value) { ports := re.FindStringSubmatch(value) if len(ports) != 1 { return 0, nil } port := ports[0] portNumber, err := strconv.ParseInt(port, k8s.Base, k8s.BitSize) if err != nil { return 0, fmt.Errorf("failed to parse port value %q: %w", port, err) } return int32(portNumber), nil } return 0, nil }