1 package component
2
3 import (
4 "context"
5 "fmt"
6 "strings"
7 "time"
8
9 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
10 "github.com/devfile/library/v2/pkg/devfile/generator"
11 "github.com/devfile/library/v2/pkg/devfile/parser"
12 "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common"
13
14 "github.com/redhat-developer/odo/pkg/configAutomount"
15 "github.com/redhat-developer/odo/pkg/dev/kubedev/storage"
16 "github.com/redhat-developer/odo/pkg/kclient"
17 odolabels "github.com/redhat-developer/odo/pkg/labels"
18 odogenerator "github.com/redhat-developer/odo/pkg/libdevfile/generator"
19 "github.com/redhat-developer/odo/pkg/log"
20 "github.com/redhat-developer/odo/pkg/util"
21
22 batchv1 "k8s.io/api/batch/v1"
23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24 "k8s.io/klog"
25 "k8s.io/utils/pointer"
26 )
27
28 func ExecuteInNewContainer(
29 ctx context.Context,
30 kubeClient kclient.ClientInterface,
31 configAutomountClient configAutomount.Client,
32 devfileObj parser.DevfileObj,
33 componentName string,
34 appName string,
35 command v1alpha2.Command,
36 ) error {
37 policy, err := kubeClient.GetCurrentNamespacePolicy()
38 if err != nil {
39 return err
40 }
41 podTemplateSpec, err := generator.GetPodTemplateSpec(devfileObj, generator.PodTemplateParams{
42 Options: common.DevfileOptions{
43 FilterByName: command.Exec.Component,
44 },
45 PodSecurityAdmissionPolicy: policy,
46 })
47 if err != nil {
48 return err
49 }
50
51 podTemplateSpec.Spec.RestartPolicy = "Never"
52
53 if len(podTemplateSpec.Spec.Containers) != 1 {
54 return fmt.Errorf("could not find the component")
55 }
56
57 podTemplateSpec.Spec.Containers[0].Command = []string{"/bin/sh"}
58 podTemplateSpec.Spec.Containers[0].Args = getJobCmdline(command)
59
60 volumes, err := storage.GetAutomountVolumes(configAutomountClient, podTemplateSpec.Spec.Containers, podTemplateSpec.Spec.InitContainers)
61 if err != nil {
62 return err
63 }
64
65 podTemplateSpec.Spec.Volumes = volumes
66
67
68
69 getJobName := func() string {
70 maxLen := kclient.JobNameOdoMaxLength - len(command.Id)
71
72 name, _ := util.NamespaceKubernetesObjectWithTrim(componentName, appName, maxLen)
73 name += "-" + command.Id
74 return name
75 }
76 completionMode := batchv1.CompletionMode("Indexed")
77 jobParams := odogenerator.JobParams{
78 TypeMeta: generator.GetTypeMeta(kclient.JobsKind, kclient.JobsAPIVersion),
79 ObjectMeta: metav1.ObjectMeta{
80 Name: getJobName(),
81 },
82 PodTemplateSpec: *podTemplateSpec,
83 SpecParams: odogenerator.JobSpecParams{
84 CompletionMode: &completionMode,
85 TTLSecondsAfterFinished: pointer.Int32(60),
86 BackOffLimit: pointer.Int32(1),
87 },
88 }
89 job := odogenerator.GetJob(jobParams)
90
91 job.SetLabels(odolabels.GetLabels(componentName, appName, GetComponentRuntimeFromDevfileMetadata(devfileObj.Data.GetMetadata()), odolabels.ComponentDeployMode, false))
92 job.Annotations = map[string]string{}
93 odolabels.AddCommonAnnotations(job.Annotations)
94 odolabels.SetProjectType(job.Annotations, GetComponentTypeFromDevfileMetadata(devfileObj.Data.GetMetadata()))
95
96
97 checkAndDeleteExistingJob := func() {
98 items, dErr := kubeClient.ListJobs(odolabels.GetSelector(componentName, appName, odolabels.ComponentDeployMode, false))
99 if dErr != nil {
100 klog.V(4).Infof("failed to list jobs; cause: %s", dErr.Error())
101 return
102 }
103 jobName := getJobName()
104 for _, item := range items.Items {
105 if strings.Contains(item.Name, jobName) {
106 dErr = kubeClient.DeleteJob(item.Name)
107 if dErr != nil {
108 klog.V(4).Infof("failed to delete job %q; cause: %s", item.Name, dErr.Error())
109 }
110 }
111 }
112 }
113 checkAndDeleteExistingJob()
114
115 log.Sectionf("Executing command:")
116 spinner := log.Spinnerf("Executing command in container (command: %s)", command.Id)
117 defer spinner.End(false)
118
119 var createdJob *batchv1.Job
120 createdJob, err = kubeClient.CreateJob(job, "")
121 if err != nil {
122 return err
123 }
124 defer func() {
125 err = kubeClient.DeleteJob(createdJob.Name)
126 if err != nil {
127 klog.V(4).Infof("failed to delete job %q; cause: %s", createdJob.Name, err)
128 }
129 }()
130
131 var done = make(chan struct{}, 1)
132
133 go func() {
134 select {
135 case <-time.After(1 * time.Minute):
136 log.Info("\nTip: Run `odo logs --deploy --follow` to get the logs of the command output.")
137 case <-done:
138 return
139 }
140 }()
141
142
143 _, err = kubeClient.WaitForJobToComplete(createdJob)
144 done <- struct{}{}
145
146 spinner.End(err == nil)
147
148 if err != nil {
149 err = fmt.Errorf("failed to execute (command: %s)", command.Id)
150
151 jobLogs, logErr := kubeClient.GetJobLogs(createdJob, command.Exec.Component)
152 if logErr != nil {
153 log.Warningf("failed to fetch the logs of execution; cause: %s", logErr)
154 }
155 fmt.Println("Execution output:")
156 _ = util.DisplayLog(false, jobLogs, log.GetStderr(), componentName, 100)
157 }
158
159 return err
160 }
161
162 func getJobCmdline(command v1alpha2.Command) []string {
163
164 var cmdLine string
165 setEnvVariable := util.GetCommandStringFromEnvs(command.Exec.Env)
166
167 if setEnvVariable == "" {
168 cmdLine = command.Exec.CommandLine
169 } else {
170 cmdLine = setEnvVariable + " && " + command.Exec.CommandLine
171 }
172 var args []string
173 if command.Exec.WorkingDir != "" {
174
175 args = []string{"-c", "cd " + command.Exec.WorkingDir + " && " + cmdLine}
176 } else {
177 args = []string{"-c", cmdLine}
178 }
179 return args
180 }
181
View as plain text