1 package kclient
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7
8 appsv1 "k8s.io/api/apps/v1"
9 corev1 "k8s.io/api/core/v1"
10 kerrors "k8s.io/apimachinery/pkg/api/errors"
11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12 "k8s.io/apimachinery/pkg/fields"
13 "k8s.io/apimachinery/pkg/runtime/schema"
14 "k8s.io/apimachinery/pkg/types"
15 "k8s.io/apimachinery/pkg/watch"
16 "k8s.io/klog"
17
18 odolabels "github.com/redhat-developer/odo/pkg/labels"
19 )
20
21 func Bool(b bool) *bool {
22 return &b
23 }
24
25 const (
26 DeploymentKind = "Deployment"
27 DeploymentAPIVersion = "apps/v1"
28 )
29
30
31 func (c *Client) GetDeploymentByName(name string) (*appsv1.Deployment, error) {
32 deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
33
34 deployment.APIVersion = DeploymentAPIVersion
35 deployment.Kind = DeploymentKind
36 return deployment, err
37 }
38
39
40 func (c *Client) GetOneDeployment(componentName, appName string, isPartOfComponent bool) (*appsv1.Deployment, error) {
41 selector := odolabels.GetSelector(componentName, appName, odolabels.ComponentDevMode, isPartOfComponent)
42 return c.GetOneDeploymentFromSelector(selector)
43 }
44
45
46
47
48
49 func (c *Client) GetOneDeploymentFromSelector(selector string) (*appsv1.Deployment, error) {
50 deployments, err := c.GetDeploymentFromSelector(selector)
51 if err != nil {
52 return nil, fmt.Errorf("unable to get Deployments for the selector: %v: %w", selector, err)
53 }
54
55 num := len(deployments)
56 if num == 0 {
57 return nil, &DeploymentNotFoundError{Selector: selector}
58 } else if num > 1 {
59 return nil, fmt.Errorf("multiple Deployments exist for the selector: %v. Only one must be present", selector)
60 }
61
62 return &deployments[0], nil
63 }
64
65
66 func (c *Client) GetDeploymentFromSelector(selector string) ([]appsv1.Deployment, error) {
67 var deploymentList *appsv1.DeploymentList
68 var err error
69
70 if selector != "" {
71 deploymentList, err = c.KubeClient.AppsV1().Deployments(c.Namespace).List(context.TODO(), metav1.ListOptions{
72 LabelSelector: selector,
73 })
74 } else {
75 deploymentList, err = c.KubeClient.AppsV1().Deployments(c.Namespace).List(context.TODO(), metav1.ListOptions{
76 FieldSelector: fields.Set{"metadata.namespace": c.Namespace}.AsSelector().String(),
77 })
78 }
79 if err != nil {
80 return nil, fmt.Errorf("unable to list Deployments: %w", err)
81 }
82 return deploymentList.Items, nil
83 }
84
85 func resourceAsJson(resource interface{}) string {
86 data, _ := json.MarshalIndent(resource, " ", " ")
87 return string(data)
88 }
89
90
91 func (c *Client) CreateDeployment(deploy appsv1.Deployment) (*appsv1.Deployment, error) {
92 deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Create(context.TODO(), &deploy, metav1.CreateOptions{FieldManager: FieldManager})
93 if err != nil {
94 return nil, fmt.Errorf("unable to create Deployment %s: %w", deploy.Name, err)
95 }
96 return deployment, nil
97 }
98
99
100 func (c *Client) UpdateDeployment(deploy appsv1.Deployment) (*appsv1.Deployment, error) {
101 deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Update(context.TODO(), &deploy, metav1.UpdateOptions{FieldManager: FieldManager})
102 if err != nil {
103 return nil, fmt.Errorf("unable to update Deployment %s: %w", deploy.Name, err)
104 }
105 return deployment, nil
106 }
107
108
109
110
111 func (c *Client) ApplyDeployment(deploy appsv1.Deployment) (*appsv1.Deployment, error) {
112 data, err := json.Marshal(deploy)
113 if err != nil {
114 return nil, fmt.Errorf("unable to marshal deployment: %w", err)
115 }
116 klog.V(5).Infoln("Applying Deployment via server-side apply:")
117 klog.V(5).Infoln(resourceAsJson(deploy))
118
119 err = c.removeDuplicateEnv(deploy.Name)
120 if err != nil {
121 return nil, err
122 }
123
124 deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Patch(context.TODO(), deploy.Name, types.ApplyPatchType, data, metav1.PatchOptions{FieldManager: FieldManager, Force: Bool(true)})
125 if err != nil {
126 return nil, fmt.Errorf("unable to update Deployment %s: %w", deploy.Name, err)
127 }
128 return deployment, nil
129 }
130
131
132
133 func (c *Client) removeDuplicateEnv(deploymentName string) error {
134 deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Get(context.Background(), deploymentName, metav1.GetOptions{})
135 if kerrors.IsNotFound(err) {
136 return nil
137 }
138 if err != nil {
139 return err
140 }
141 changes := false
142 containers := deployment.Spec.Template.Spec.Containers
143 for i := range containers {
144 found := map[string]bool{}
145 var newEnv []corev1.EnvVar
146 for _, env := range containers[i].Env {
147 if _, ok := found[env.Name]; !ok {
148 found[env.Name] = true
149 newEnv = append(newEnv, env)
150 } else {
151 changes = true
152 }
153 }
154 containers[i].Env = newEnv
155 }
156 if changes {
157 _, err = c.KubeClient.AppsV1().Deployments(c.Namespace).Update(context.Background(), deployment, metav1.UpdateOptions{})
158 if kerrors.IsNotFound(err) {
159 return nil
160 }
161 return err
162 }
163 return nil
164 }
165
166
167
168 func (c *Client) GetDeploymentAPIVersion() (schema.GroupVersionKind, error) {
169 extV1Beta1, err := c.IsDeploymentExtensionsV1Beta1()
170 if err != nil {
171 return schema.GroupVersionKind{}, err
172 }
173
174 if extV1Beta1 {
175
176 return schema.GroupVersionKind{
177 Group: "extensions",
178 Version: "v1beta1",
179 Kind: "Deployment",
180 }, nil
181 }
182
183 return schema.GroupVersionKind{
184 Group: "apps",
185 Version: "v1",
186 Kind: "Deployment",
187 }, nil
188 }
189
190 func (c *Client) IsDeploymentExtensionsV1Beta1() (bool, error) {
191 return c.IsResourceSupported("extensions", "v1beta1", "deployments")
192 }
193
194
195
196 func (c *Client) DeploymentWatcher(ctx context.Context, selector string) (watch.Interface, error) {
197 ns := c.GetCurrentNamespace()
198 return c.GetClient().AppsV1().Deployments(ns).
199 Watch(ctx, metav1.ListOptions{
200 LabelSelector: selector,
201 })
202 }
203
View as plain text