github.com/spotahome/redis-operator@v1.2.4/service/k8s/deployment.go (about)

     1  package k8s
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	appsv1 "k8s.io/api/apps/v1"
     9  	corev1 "k8s.io/api/core/v1"
    10  	"k8s.io/apimachinery/pkg/api/errors"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/client-go/kubernetes"
    13  
    14  	"github.com/spotahome/redis-operator/log"
    15  	"github.com/spotahome/redis-operator/metrics"
    16  )
    17  
    18  // Deployment the Deployment service that knows how to interact with k8s to manage them
    19  type Deployment interface {
    20  	GetDeployment(namespace, name string) (*appsv1.Deployment, error)
    21  	GetDeploymentPods(namespace, name string) (*corev1.PodList, error)
    22  	CreateDeployment(namespace string, deployment *appsv1.Deployment) error
    23  	UpdateDeployment(namespace string, deployment *appsv1.Deployment) error
    24  	CreateOrUpdateDeployment(namespace string, deployment *appsv1.Deployment) error
    25  	DeleteDeployment(namespace string, name string) error
    26  	ListDeployments(namespace string) (*appsv1.DeploymentList, error)
    27  }
    28  
    29  // DeploymentService is the service account service implementation using API calls to kubernetes.
    30  type DeploymentService struct {
    31  	kubeClient      kubernetes.Interface
    32  	logger          log.Logger
    33  	metricsRecorder metrics.Recorder
    34  }
    35  
    36  // NewDeploymentService returns a new Deployment KubeService.
    37  func NewDeploymentService(kubeClient kubernetes.Interface, logger log.Logger, metricsRecorder metrics.Recorder) *DeploymentService {
    38  	logger = logger.With("service", "k8s.deployment")
    39  	return &DeploymentService{
    40  		kubeClient:      kubeClient,
    41  		logger:          logger,
    42  		metricsRecorder: metricsRecorder,
    43  	}
    44  }
    45  
    46  // GetDeployment will retrieve the requested deployment based on namespace and name
    47  func (d *DeploymentService) GetDeployment(namespace, name string) (*appsv1.Deployment, error) {
    48  	deployment, err := d.kubeClient.AppsV1().Deployments(namespace).Get(context.TODO(), name, metav1.GetOptions{})
    49  	recordMetrics(namespace, "Deployment", name, "GET", err, d.metricsRecorder)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	return deployment, err
    54  }
    55  
    56  // GetDeploymentPods will retrieve the pods managed by a given deployment
    57  func (d *DeploymentService) GetDeploymentPods(namespace, name string) (*corev1.PodList, error) {
    58  	deployment, err := d.kubeClient.AppsV1().Deployments(namespace).Get(context.TODO(), name, metav1.GetOptions{})
    59  	recordMetrics(namespace, "Deployment", name, "GET", err, d.metricsRecorder)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	labels := []string{}
    64  	for k, v := range deployment.Spec.Selector.MatchLabels {
    65  		labels = append(labels, fmt.Sprintf("%s=%s", k, v))
    66  	}
    67  	selector := strings.Join(labels, ",")
    68  	return d.kubeClient.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector})
    69  }
    70  
    71  // CreateDeployment will create the given deployment
    72  func (d *DeploymentService) CreateDeployment(namespace string, deployment *appsv1.Deployment) error {
    73  	_, err := d.kubeClient.AppsV1().Deployments(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
    74  	recordMetrics(namespace, "Deployment", deployment.GetName(), "CREATE", err, d.metricsRecorder)
    75  	if err != nil {
    76  		return err
    77  	}
    78  	d.logger.WithField("namespace", namespace).WithField("deployment", deployment.ObjectMeta.Name).Debugf("deployment created")
    79  	return err
    80  }
    81  
    82  // UpdateDeployment will update the given deployment
    83  func (d *DeploymentService) UpdateDeployment(namespace string, deployment *appsv1.Deployment) error {
    84  	_, err := d.kubeClient.AppsV1().Deployments(namespace).Update(context.TODO(), deployment, metav1.UpdateOptions{})
    85  	recordMetrics(namespace, "Deployment", deployment.GetName(), "UPDATE", err, d.metricsRecorder)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	d.logger.WithField("namespace", namespace).WithField("deployment", deployment.ObjectMeta.Name).Debugf("deployment updated")
    90  	return err
    91  }
    92  
    93  // CreateOrUpdateDeployment will update the given deployment or create it if does not exist
    94  func (d *DeploymentService) CreateOrUpdateDeployment(namespace string, deployment *appsv1.Deployment) error {
    95  	storedDeployment, err := d.GetDeployment(namespace, deployment.Name)
    96  	if err != nil {
    97  		// If no resource we need to create.
    98  		if errors.IsNotFound(err) {
    99  			return d.CreateDeployment(namespace, deployment)
   100  		}
   101  		return err
   102  	}
   103  
   104  	// Already exists, need to Update.
   105  	// Set the correct resource version to ensure we are on the latest version. This way the only valid
   106  	// namespace is our spec(https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#concurrency-control-and-consistency),
   107  	// we will replace the current namespace state.
   108  	deployment.ResourceVersion = storedDeployment.ResourceVersion
   109  	return d.UpdateDeployment(namespace, deployment)
   110  }
   111  
   112  // DeleteDeployment will delete the given deployment
   113  func (d *DeploymentService) DeleteDeployment(namespace, name string) error {
   114  	propagation := metav1.DeletePropagationForeground
   115  	err := d.kubeClient.AppsV1().Deployments(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{PropagationPolicy: &propagation})
   116  	recordMetrics(namespace, "Deployment", name, "DELETE", err, d.metricsRecorder)
   117  	return err
   118  }
   119  
   120  // ListDeployments will give all the deployments on a given namespace
   121  func (d *DeploymentService) ListDeployments(namespace string) (*appsv1.DeploymentList, error) {
   122  	deployments, err := d.kubeClient.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
   123  	recordMetrics(namespace, "Deployment", metrics.NOT_APPLICABLE, "LIST", err, d.metricsRecorder)
   124  	return deployments, err
   125  }