1 package delete
2
3 import (
4 "context"
5 "fmt"
6 "path/filepath"
7
8 "github.com/devfile/library/v2/pkg/devfile/parser"
9 v1 "k8s.io/api/apps/v1"
10 corev1 "k8s.io/api/core/v1"
11 kerrors "k8s.io/apimachinery/pkg/api/errors"
12 "k8s.io/apimachinery/pkg/api/meta"
13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
15 "k8s.io/klog"
16
17 "github.com/redhat-developer/odo/pkg/component"
18 "github.com/redhat-developer/odo/pkg/configAutomount"
19 "github.com/redhat-developer/odo/pkg/exec"
20 "github.com/redhat-developer/odo/pkg/kclient"
21 odolabels "github.com/redhat-developer/odo/pkg/labels"
22 "github.com/redhat-developer/odo/pkg/libdevfile"
23 "github.com/redhat-developer/odo/pkg/log"
24 clierrors "github.com/redhat-developer/odo/pkg/odo/cli/errors"
25 odocontext "github.com/redhat-developer/odo/pkg/odo/context"
26 "github.com/redhat-developer/odo/pkg/platform"
27 "github.com/redhat-developer/odo/pkg/podman"
28 "github.com/redhat-developer/odo/pkg/util"
29 )
30
31 type DeleteComponentClient struct {
32 kubeClient kclient.ClientInterface
33 podmanClient podman.Client
34 execClient exec.Client
35 configAutomountClient configAutomount.Client
36 }
37
38 var _ Client = (*DeleteComponentClient)(nil)
39
40 func NewDeleteComponentClient(
41 kubeClient kclient.ClientInterface,
42 podmanClient podman.Client,
43 execClient exec.Client,
44 configAutomountClient configAutomount.Client,
45 ) *DeleteComponentClient {
46 return &DeleteComponentClient{
47 kubeClient: kubeClient,
48 podmanClient: podmanClient,
49 execClient: execClient,
50 configAutomountClient: configAutomountClient,
51 }
52 }
53
54
55
56 func (do *DeleteComponentClient) ListClusterResourcesToDelete(
57 ctx context.Context,
58 componentName string,
59 namespace string,
60 mode string,
61 ) ([]unstructured.Unstructured, error) {
62 var result []unstructured.Unstructured
63 selector := odolabels.GetSelector(componentName, odocontext.GetApplication(ctx), mode, false)
64 list, err := do.kubeClient.GetAllResourcesFromSelector(selector, namespace)
65 if err != nil {
66 return nil, err
67 }
68 for _, resource := range list {
69
70 if resource.GetDeletionTimestamp() != nil {
71 continue
72 }
73 referenced := false
74 for _, ownerRef := range resource.GetOwnerReferences() {
75 if references(list, ownerRef) {
76 referenced = true
77 break
78 }
79 }
80 if !referenced {
81 result = append(result, resource)
82 }
83 }
84
85 return result, nil
86 }
87
88 func (do *DeleteComponentClient) DeleteResources(resources []unstructured.Unstructured, wait bool) []unstructured.Unstructured {
89 var failed []unstructured.Unstructured
90 for _, resource := range resources {
91 gvr, err := do.kubeClient.GetRestMappingFromUnstructured(resource)
92 if err != nil {
93 failed = append(failed, resource)
94 continue
95 }
96 err = do.kubeClient.DeleteDynamicResource(resource.GetName(), gvr.Resource, wait)
97 if err != nil && !kerrors.IsNotFound(err) {
98 klog.V(3).Infof("failed to delete resource %q (%s.%s.%s): %v", resource.GetName(), gvr.Resource.Group, gvr.Resource.Version, gvr.Resource.Resource, err)
99 failed = append(failed, resource)
100 }
101 }
102 return failed
103 }
104
105
106 func references(list []unstructured.Unstructured, ownerRef metav1.OwnerReference) bool {
107 for _, resource := range list {
108 if ownerRef.APIVersion == resource.GetAPIVersion() && ownerRef.Kind == resource.GetKind() && ownerRef.Name == resource.GetName() {
109 return true
110 }
111 }
112 return false
113 }
114
115
116
117 func (do DeleteComponentClient) ListClusterResourcesToDeleteFromDevfile(devfileObj parser.DevfileObj, appName string, componentName string, mode string) (isInnerLoopDeployed bool, resources []unstructured.Unstructured, err error) {
118 var deployment *v1.Deployment
119 if mode == odolabels.ComponentDevMode || mode == odolabels.ComponentAnyMode {
120
121 var deploymentName string
122 deploymentName, err = util.NamespaceKubernetesObject(componentName, appName)
123 if err != nil {
124 return isInnerLoopDeployed, resources, fmt.Errorf("failed to get the resource %q name for component %q; cause: %w", kclient.DeploymentKind, componentName, err)
125 }
126
127 deployment, err = do.kubeClient.GetDeploymentByName(deploymentName)
128 if err != nil && !kerrors.IsNotFound(err) {
129
130 err = clierrors.NewWarning(fmt.Sprintf("failed to get deployment %q", deploymentName), err)
131 return isInnerLoopDeployed, resources, err
132 }
133
134
135
136
137 if deployment.Name != "" {
138 isInnerLoopDeployed = true
139 var unstructuredDeploy unstructured.Unstructured
140 unstructuredDeploy, err = kclient.ConvertK8sResourceToUnstructured(deployment)
141 if err != nil {
142 return isInnerLoopDeployed, resources, fmt.Errorf("failed to parse the resource %q: %q; cause: %w", kclient.DeploymentKind, deploymentName, err)
143 }
144 resources = append(resources, unstructuredDeploy)
145 }
146 }
147
148
149 localK8sResources, err := libdevfile.ListKubernetesComponents(devfileObj, filepath.Dir(devfileObj.Ctx.GetAbsPath()))
150 if err != nil {
151 return isInnerLoopDeployed, resources, fmt.Errorf("failed to gather resources for deletion: %w", err)
152 }
153 localOCResources, err := libdevfile.ListOpenShiftComponents(devfileObj, filepath.Dir(devfileObj.Ctx.GetAbsPath()))
154 if err != nil {
155 return isInnerLoopDeployed, resources, fmt.Errorf("failed to gather resources for deletion: %w", err)
156 }
157
158 localAllResources := []unstructured.Unstructured{}
159 localAllResources = append(localAllResources, localOCResources...)
160 localAllResources = append(localAllResources, localK8sResources...)
161
162 for _, lr := range localAllResources {
163 var gvr *meta.RESTMapping
164 gvr, err = do.kubeClient.GetRestMappingFromUnstructured(lr)
165 if err != nil {
166 continue
167 }
168
169 var cr *unstructured.Unstructured
170 cr, err = do.kubeClient.GetDynamicResource(gvr.Resource, lr.GetName())
171
172 if err != nil || (mode != odolabels.ComponentAnyMode && odolabels.GetMode(cr.GetLabels()) != mode) {
173 if cr != nil {
174 klog.V(4).Infof("Ignoring resource: %s/%s; its mode(%s) does not match with the given mode(%s)", gvr.Resource.Resource, lr.GetName(), odolabels.GetMode(cr.GetLabels()), mode)
175 } else {
176 klog.V(4).Infof("Ignoring resource: %s/%s; it does not exist on the cluster", gvr.Resource.Resource, lr.GetName())
177 }
178 continue
179 }
180 resources = append(resources, *cr)
181 }
182
183 return isInnerLoopDeployed, resources, nil
184 }
185
186
187 func (do *DeleteComponentClient) ExecutePreStopEvents(ctx context.Context, devfileObj parser.DevfileObj, appName string, componentName string) error {
188 if !libdevfile.HasPreStopEvents(devfileObj) {
189 return nil
190 }
191
192 klog.V(4).Infof("Gathering information for component: %q", componentName)
193
194 klog.V(3).Infof("Checking component status for %q", componentName)
195 selector := odolabels.GetSelector(componentName, appName, odolabels.ComponentDevMode, false)
196 pod, err := do.kubeClient.GetRunningPodFromSelector(selector)
197 if err != nil {
198 klog.V(1).Info("Component not found on the cluster.")
199
200 if kerrors.IsForbidden(err) {
201 klog.V(3).Infof("Resource for %q forbidden", componentName)
202 log.Warningf("You are forbidden from accessing the resource. Please check if you the right permissions and try again.")
203 return nil
204 }
205
206 if e, ok := err.(*platform.PodNotFoundError); ok {
207 klog.V(3).Infof("Resource for %q not found; cause: %v", componentName, e)
208 log.Warningf("Resources not found on the cluster. Run `odo delete component -v <DEBUG_LEVEL_0-9>` to know more.")
209 return nil
210 }
211
212 return fmt.Errorf("unable to determine if component %s exists; cause: %v", componentName, err.Error())
213 }
214
215 klog.V(4).Infof("Executing %q event commands for component %q", libdevfile.PreStop, componentName)
216
217 handler := component.NewRunHandler(
218 ctx,
219 do.kubeClient,
220 do.execClient,
221 do.configAutomountClient,
222
223 nil, nil,
224
225 component.HandlerOptions{
226 PodName: pod.Name,
227 ContainersRunning: component.GetContainersNames(pod),
228 Msg: "Executing pre-stop command in container",
229 },
230 )
231 err = libdevfile.ExecPreStopEvents(ctx, devfileObj, handler)
232 if err != nil {
233 log.Warningf("Failed to execute %q event commands for component %q, cause: %v", libdevfile.PreStop, componentName, err.Error())
234 }
235
236 return nil
237 }
238
239 func (do *DeleteComponentClient) ListPodmanResourcesToDelete(appName string, componentName string, mode string) (isInnerLoopDeployed bool, pods []*corev1.Pod, err error) {
240 if mode == odolabels.ComponentDeployMode {
241 return false, nil, nil
242 }
243
244
245 var podName string
246 podName, err = util.NamespaceKubernetesObject(componentName, appName)
247 if err != nil {
248 return false, nil, fmt.Errorf("failed to get the resource %q name for component %q; cause: %w", kclient.DeploymentKind, componentName, err)
249 }
250
251 allPods, err := do.podmanClient.PodLs()
252 if err != nil {
253 err = clierrors.NewWarning("failed to get pods on podman", err)
254 return false, nil, err
255 }
256
257 if _, isInnerLoopDeployed = allPods[podName]; isInnerLoopDeployed {
258 podDef, err := do.podmanClient.KubeGenerate(podName)
259 if err != nil {
260 return false, nil, err
261 }
262 pods = append(pods, podDef)
263 }
264 return isInnerLoopDeployed, pods, nil
265 }
266
View as plain text