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 }