mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
Support exec command (#6579)
* Support exec command for deploy Signed-off-by: Parthvi Vala <pvala@redhat.com> * Print log after timeout Signed-off-by: Parthvi Vala <pvala@redhat.com> * Add helper function to form proper commandLine Signed-off-by: Parthvi Vala <pvala@redhat.com> * Mockgen kclient Signed-off-by: Parthvi Vala <pvala@redhat.com> * Enhance error message Signed-off-by: Parthvi Vala <pvala@redhat.com> * Attempt at fixing unit test failures Signed-off-by: Parthvi Vala <pvala@redhat.com> * Rename import v1 to batchv1 Signed-off-by: Parthvi Vala <pvala@redhat.com> * Remove TODOs Signed-off-by: Parthvi Vala <pvala@redhat.com> * Add integration tests and cleanup on user interrupt Signed-off-by: Parthvi Vala <pvala@redhat.com> * Temp changes Signed-off-by: Parthvi Vala <pvala@redhat.com> * Log tip to run odo logs after a minute Signed-off-by: Parthvi Vala <pvala@redhat.com> * List components to delete even if there are no devfile resources Signed-off-by: Parthvi Vala <pvala@redhat.com> * Fix integration tests Signed-off-by: Parthvi Vala <pvala@redhat.com> * Fix deploy exec delete integration test Signed-off-by: Parthvi Vala <pvala@redhat.com> * Temp Change * Fix delete command tests * Fix mockgen client * Fix validation errors Signed-off-by: Parthvi Vala <pvala@redhat.com> * Fix unit test failure Signed-off-by: Parthvi Vala <pvala@redhat.com> * Attemp at writing less flaky integration test Signed-off-by: Parthvi Vala <pvala@redhat.com> * Remove TODOs Signed-off-by: Parthvi Vala <pvala@redhat.com> * Add tip after 1 minute and return the go routine if job finishes before that Signed-off-by: Parthvi Vala <pvala@redhat.com> * Use the container as it is so that container-overrides can be taken into account Signed-off-by: Parthvi Vala <pvala@redhat.com> * Move job spec code to a different helper function inside the libdevfile pkg Signed-off-by: Parthvi Vala <pvala@redhat.com> * Modify the Execute method to use the new helper function and refactoring Signed-off-by: Parthvi Vala <pvala@redhat.com> * Attempt at fixing integration and unit tests Signed-off-by: Parthvi Vala <pvala@redhat.com> * Move defer to print remaining resources to a separate function, fix func doc and gofmt the files Signed-off-by: Parthvi Vala <pvala@redhat.com> * Fix test failures Signed-off-by: Parthvi Vala <pvala@redhat.com> * Cleanup Signed-off-by: Parthvi Vala <pvala@redhat.com> * Cleanup unused functions Signed-off-by: Parthvi Vala <pvala@redhat.com> --------- Signed-off-by: Parthvi Vala <pvala@redhat.com>
This commit is contained in:
@@ -2,19 +2,31 @@ package deploy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
"github.com/devfile/library/v2/pkg/devfile/generator"
|
||||
"github.com/devfile/library/v2/pkg/devfile/parser"
|
||||
"github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/image"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
odolabels "github.com/redhat-developer/odo/pkg/labels"
|
||||
"github.com/redhat-developer/odo/pkg/libdevfile"
|
||||
odogenerator "github.com/redhat-developer/odo/pkg/libdevfile/generator"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
)
|
||||
|
||||
type DeployClient struct {
|
||||
@@ -83,10 +95,135 @@ func (o *deployHandler) ApplyOpenShift(openshift v1alpha2.Component) error {
|
||||
}
|
||||
|
||||
// Execute will deploy the listed information in the `exec` section of devfile.yaml
|
||||
// We currently do NOT support this in `odo deploy`.
|
||||
func (o *deployHandler) Execute(command v1alpha2.Command) error {
|
||||
// TODO:
|
||||
// * Make sure we inject the "deploy" mode label once we implement exec in `odo deploy`
|
||||
// * Make sure you inject the "component type" label once we implement exec.
|
||||
return errors.New("exec command is not implemented for Deploy")
|
||||
containerComps, err := generator.GetContainers(o.devfileObj, common.DevfileOptions{FilterByName: command.Exec.Component})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(containerComps) != 1 {
|
||||
return fmt.Errorf("could not find the component")
|
||||
}
|
||||
|
||||
containerComp := containerComps[0]
|
||||
containerComp.Command = []string{"/bin/sh"}
|
||||
containerComp.Args = getCmdline(command)
|
||||
|
||||
// Create a Kubernetes Job and use the container image referenced by command.Exec.Component
|
||||
// Get the component for the command with command.Exec.Component
|
||||
getJobName := func() string {
|
||||
maxLen := kclient.JobNameOdoMaxLength - len(command.Id)
|
||||
// We ignore the error here because our component name or app name will never be empty; which are the only cases when an error might be raised.
|
||||
name, _ := util.NamespaceKubernetesObjectWithTrim(o.componentName, o.appName, maxLen)
|
||||
name += "-" + command.Id
|
||||
return name
|
||||
}
|
||||
completionMode := batchv1.CompletionMode("Indexed")
|
||||
jobParams := odogenerator.JobParams{
|
||||
TypeMeta: generator.GetTypeMeta(kclient.JobsKind, kclient.JobsAPIVersion),
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: getJobName(),
|
||||
},
|
||||
PodTemplateSpec: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{containerComp},
|
||||
// Setting the restart policy to "never" so that pods are kept around after the job finishes execution; this is helpful in obtaining logs to debug.
|
||||
RestartPolicy: "Never",
|
||||
},
|
||||
},
|
||||
SpecParams: odogenerator.JobSpecParams{
|
||||
CompletionMode: &completionMode,
|
||||
TTLSecondsAfterFinished: pointer.Int32(60),
|
||||
BackOffLimit: pointer.Int32(1),
|
||||
},
|
||||
}
|
||||
job := odogenerator.GetJob(jobParams)
|
||||
// Set labels and annotations
|
||||
job.SetLabels(odolabels.GetLabels(o.componentName, o.appName, component.GetComponentRuntimeFromDevfileMetadata(o.devfileObj.Data.GetMetadata()), odolabels.ComponentDeployMode, false))
|
||||
job.Annotations = map[string]string{}
|
||||
odolabels.AddCommonAnnotations(job.Annotations)
|
||||
odolabels.SetProjectType(job.Annotations, component.GetComponentTypeFromDevfileMetadata(o.devfileObj.Data.GetMetadata()))
|
||||
|
||||
// Make sure there are no existing jobs
|
||||
checkAndDeleteExistingJob := func() {
|
||||
items, dErr := o.kubeClient.ListJobs(odolabels.GetSelector(o.componentName, o.appName, odolabels.ComponentDeployMode, false))
|
||||
if dErr != nil {
|
||||
klog.V(4).Infof("failed to list jobs; cause: %s", dErr.Error())
|
||||
return
|
||||
}
|
||||
jobName := getJobName()
|
||||
for _, item := range items.Items {
|
||||
if strings.Contains(item.Name, jobName) {
|
||||
dErr = o.kubeClient.DeleteJob(item.Name)
|
||||
if dErr != nil {
|
||||
klog.V(4).Infof("failed to delete job %q; cause: %s", item.Name, dErr.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkAndDeleteExistingJob()
|
||||
|
||||
log.Sectionf("Executing command:")
|
||||
spinner := log.Spinnerf("Executing command in container (command: %s)", command.Id)
|
||||
defer spinner.End(false)
|
||||
|
||||
var createdJob *batchv1.Job
|
||||
createdJob, err = o.kubeClient.CreateJob(job, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = o.kubeClient.DeleteJob(createdJob.Name)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("failed to delete job %q; cause: %s", createdJob.Name, err)
|
||||
}
|
||||
}()
|
||||
|
||||
var done = make(chan struct{}, 1)
|
||||
// Print the tip to use `odo logs` if the command is still running after 1 minute
|
||||
go func() {
|
||||
select {
|
||||
case <-time.After(1 * time.Minute):
|
||||
log.Info("\nTip: Run `odo logs --deploy --follow` to get the logs of the command output.")
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for the command to complete execution
|
||||
_, err = o.kubeClient.WaitForJobToComplete(createdJob)
|
||||
done <- struct{}{}
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to execute (command: %s)", command.Id)
|
||||
// Print the job logs if the job failed
|
||||
jobLogs, logErr := o.kubeClient.GetJobLogs(createdJob, command.Exec.Component)
|
||||
if logErr != nil {
|
||||
log.Warningf("failed to fetch the logs of execution; cause: %s", logErr)
|
||||
}
|
||||
fmt.Println("Execution output:")
|
||||
_ = util.DisplayLog(false, jobLogs, log.GetStderr(), o.componentName, 100)
|
||||
}
|
||||
|
||||
spinner.End(err == nil)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func getCmdline(command v1alpha2.Command) []string {
|
||||
// deal with environment variables
|
||||
var cmdLine string
|
||||
setEnvVariable := util.GetCommandStringFromEnvs(command.Exec.Env)
|
||||
|
||||
if setEnvVariable == "" {
|
||||
cmdLine = command.Exec.CommandLine
|
||||
} else {
|
||||
cmdLine = setEnvVariable + " && " + command.Exec.CommandLine
|
||||
}
|
||||
var args []string
|
||||
if command.Exec.WorkingDir != "" {
|
||||
// since we are using /bin/sh -c, the command needs to be within a single double quote instance, for example "cd /tmp && pwd"
|
||||
args = []string{"-c", "cd " + command.Exec.WorkingDir + " && " + cmdLine}
|
||||
} else {
|
||||
args = []string{"-c", cmdLine}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
@@ -484,7 +484,7 @@ func (a *Adapter) createOrUpdateComponent(
|
||||
serviceAnnotations["service.binding/backend_ip"] = "path={.spec.clusterIP}"
|
||||
serviceAnnotations["service.binding/backend_port"] = "path={.spec.ports},elementType=sliceOfMaps,sourceKey=name,sourceValue=port"
|
||||
|
||||
serviceName, err := util.NamespaceKubernetesObjectWithTrim(componentName, a.AppName)
|
||||
serviceName, err := util.NamespaceKubernetesObjectWithTrim(componentName, a.AppName, 63)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
projectv1 "github.com/openshift/api/project/v1"
|
||||
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
@@ -162,4 +163,13 @@ type ClientInterface interface {
|
||||
|
||||
// ingress_routes.go
|
||||
ListIngresses(namespace, selector string) (*v1.IngressList, error)
|
||||
|
||||
ListJobs(selector string) (*batchv1.JobList, error)
|
||||
// CreateJob creates a K8s job to execute task
|
||||
CreateJob(job batchv1.Job, namespace string) (*batchv1.Job, error)
|
||||
// WaitForJobToComplete to wait until a job completes or fails; it starts printing log or error if the job does not complete execution after 1 minute
|
||||
WaitForJobToComplete(job *batchv1.Job) (*batchv1.Job, error)
|
||||
// GetJobLogs retrieves pod logs of a job
|
||||
GetJobLogs(job *batchv1.Job, containerName string) (io.ReadCloser, error)
|
||||
DeleteJob(jobName string) error
|
||||
}
|
||||
|
||||
98
pkg/kclient/jobs.go
Normal file
98
pkg/kclient/jobs.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package kclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// constants for volumes
|
||||
const (
|
||||
JobsKind = "Job"
|
||||
JobsAPIVersion = "batch/v1"
|
||||
// JobNameOdoMaxLength is the max length of a job name
|
||||
// To be on the safe side, we keep the max length less than the original(k8s) max length;
|
||||
// we do this because k8s job in odo is created to run exec commands in Deploy mode and this is not a user created resource,
|
||||
// so we do not want to break because of any error with job
|
||||
JobNameOdoMaxLength = 60
|
||||
)
|
||||
|
||||
func (c *Client) ListJobs(selector string) (*batchv1.JobList, error) {
|
||||
return c.KubeClient.BatchV1().Jobs(c.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector})
|
||||
}
|
||||
|
||||
// CreateJobs creates a K8s job to execute task
|
||||
func (c *Client) CreateJob(job batchv1.Job, namespace string) (*batchv1.Job, error) {
|
||||
if namespace == "" {
|
||||
namespace = c.Namespace
|
||||
}
|
||||
createdJob, err := c.KubeClient.BatchV1().Jobs(namespace).Create(context.TODO(), &job, metav1.CreateOptions{FieldManager: FieldManager})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create Jobs: %w", err)
|
||||
}
|
||||
return createdJob, nil
|
||||
}
|
||||
|
||||
// WaitForJobToComplete to wait until a job completes or fails; it starts printing log or error if the job does not complete execution after 2 minutes
|
||||
func (c *Client) WaitForJobToComplete(job *batchv1.Job) (*batchv1.Job, error) {
|
||||
klog.V(3).Infof("Waiting for Job %s to complete successfully", job.Name)
|
||||
|
||||
w, err := c.KubeClient.BatchV1().Jobs(c.Namespace).Watch(context.TODO(), metav1.ListOptions{
|
||||
FieldSelector: fields.Set{"metadata.name": job.Name}.AsSelector().String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to watch job: %w", err)
|
||||
}
|
||||
defer w.Stop()
|
||||
|
||||
for {
|
||||
val, ok := <-w.ResultChan()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
wJob, ok := val.Object.(*batchv1.Job)
|
||||
if !ok {
|
||||
klog.V(4).Infof("did not receive job object, received: %v", val)
|
||||
continue
|
||||
}
|
||||
for _, condition := range wJob.Status.Conditions {
|
||||
if condition.Type == batchv1.JobFailed {
|
||||
klog.V(4).Infof("Failed to execute the job, reason: %s", condition.String())
|
||||
// we return the job as it is in case the caller requires it for further investigation.
|
||||
return wJob, fmt.Errorf("failed to execute the job")
|
||||
}
|
||||
if condition.Type == batchv1.JobComplete {
|
||||
return wJob, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetJobLogs retrieves pod logs of a job
|
||||
func (c *Client) GetJobLogs(job *batchv1.Job, containerName string) (io.ReadCloser, error) {
|
||||
// Set standard log options
|
||||
// RESTClient call to kubernetes
|
||||
selector := labels.Set{"controller-uid": string(job.UID), "job-name": job.Name}.AsSelector().String()
|
||||
pods, err := c.GetPodsMatchingSelector(selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pods.Items) == 0 {
|
||||
return nil, fmt.Errorf("no pod found for job %q", job.Name)
|
||||
}
|
||||
pod := pods.Items[0]
|
||||
return c.GetPodLogs(pod.Name, containerName, false)
|
||||
}
|
||||
|
||||
func (c *Client) DeleteJob(jobName string) error {
|
||||
propagationPolicy := metav1.DeletePropagationBackground
|
||||
return c.KubeClient.BatchV1().Jobs(c.Namespace).Delete(context.Background(), jobName, metav1.DeleteOptions{PropagationPolicy: &propagationPolicy})
|
||||
}
|
||||
@@ -17,10 +17,11 @@ import (
|
||||
v1alpha10 "github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1"
|
||||
v1alpha3 "github.com/redhat-developer/service-binding-operator/apis/spec/v1alpha3"
|
||||
v10 "k8s.io/api/apps/v1"
|
||||
v11 "k8s.io/api/core/v1"
|
||||
v12 "k8s.io/api/networking/v1"
|
||||
v11 "k8s.io/api/batch/v1"
|
||||
v12 "k8s.io/api/core/v1"
|
||||
v13 "k8s.io/api/networking/v1"
|
||||
meta "k8s.io/apimachinery/pkg/api/meta"
|
||||
v13 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v14 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
@@ -84,11 +85,26 @@ func (mr *MockClientInterfaceMockRecorder) CreateDeployment(deploy interface{})
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDeployment", reflect.TypeOf((*MockClientInterface)(nil).CreateDeployment), deploy)
|
||||
}
|
||||
|
||||
// CreateJob mocks base method.
|
||||
func (m *MockClientInterface) CreateJob(job v11.Job, namespace string) (*v11.Job, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateJob", job, namespace)
|
||||
ret0, _ := ret[0].(*v11.Job)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// CreateJob indicates an expected call of CreateJob.
|
||||
func (mr *MockClientInterfaceMockRecorder) CreateJob(job, namespace interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateJob", reflect.TypeOf((*MockClientInterface)(nil).CreateJob), job, namespace)
|
||||
}
|
||||
|
||||
// CreateNamespace mocks base method.
|
||||
func (m *MockClientInterface) CreateNamespace(name string) (*v11.Namespace, error) {
|
||||
func (m *MockClientInterface) CreateNamespace(name string) (*v12.Namespace, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateNamespace", name)
|
||||
ret0, _ := ret[0].(*v11.Namespace)
|
||||
ret0, _ := ret[0].(*v12.Namespace)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -114,10 +130,10 @@ func (mr *MockClientInterfaceMockRecorder) CreateNewProject(projectName, wait in
|
||||
}
|
||||
|
||||
// CreatePVC mocks base method.
|
||||
func (m *MockClientInterface) CreatePVC(pvc v11.PersistentVolumeClaim) (*v11.PersistentVolumeClaim, error) {
|
||||
func (m *MockClientInterface) CreatePVC(pvc v12.PersistentVolumeClaim) (*v12.PersistentVolumeClaim, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreatePVC", pvc)
|
||||
ret0, _ := ret[0].(*v11.PersistentVolumeClaim)
|
||||
ret0, _ := ret[0].(*v12.PersistentVolumeClaim)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -129,7 +145,7 @@ func (mr *MockClientInterfaceMockRecorder) CreatePVC(pvc interface{}) *gomock.Ca
|
||||
}
|
||||
|
||||
// CreateSecret mocks base method.
|
||||
func (m *MockClientInterface) CreateSecret(objectMeta v13.ObjectMeta, data map[string]string, ownerReference v13.OwnerReference) error {
|
||||
func (m *MockClientInterface) CreateSecret(objectMeta v14.ObjectMeta, data map[string]string, ownerReference v14.OwnerReference) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateSecret", objectMeta, data, ownerReference)
|
||||
ret0, _ := ret[0].(error)
|
||||
@@ -143,7 +159,7 @@ func (mr *MockClientInterfaceMockRecorder) CreateSecret(objectMeta, data, ownerR
|
||||
}
|
||||
|
||||
// CreateSecrets mocks base method.
|
||||
func (m *MockClientInterface) CreateSecrets(componentName string, commonObjectMeta v13.ObjectMeta, svc *v11.Service, ownerReference v13.OwnerReference) error {
|
||||
func (m *MockClientInterface) CreateSecrets(componentName string, commonObjectMeta v14.ObjectMeta, svc *v12.Service, ownerReference v14.OwnerReference) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateSecrets", componentName, commonObjectMeta, svc, ownerReference)
|
||||
ret0, _ := ret[0].(error)
|
||||
@@ -157,10 +173,10 @@ func (mr *MockClientInterfaceMockRecorder) CreateSecrets(componentName, commonOb
|
||||
}
|
||||
|
||||
// CreateService mocks base method.
|
||||
func (m *MockClientInterface) CreateService(svc v11.Service) (*v11.Service, error) {
|
||||
func (m *MockClientInterface) CreateService(svc v12.Service) (*v12.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateService", svc)
|
||||
ret0, _ := ret[0].(*v11.Service)
|
||||
ret0, _ := ret[0].(*v12.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -172,10 +188,10 @@ func (mr *MockClientInterfaceMockRecorder) CreateService(svc interface{}) *gomoc
|
||||
}
|
||||
|
||||
// CreateTLSSecret mocks base method.
|
||||
func (m *MockClientInterface) CreateTLSSecret(tlsCertificate, tlsPrivKey []byte, objectMeta v13.ObjectMeta) (*v11.Secret, error) {
|
||||
func (m *MockClientInterface) CreateTLSSecret(tlsCertificate, tlsPrivKey []byte, objectMeta v14.ObjectMeta) (*v12.Secret, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateTLSSecret", tlsCertificate, tlsPrivKey, objectMeta)
|
||||
ret0, _ := ret[0].(*v11.Secret)
|
||||
ret0, _ := ret[0].(*v12.Secret)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -200,6 +216,20 @@ func (mr *MockClientInterfaceMockRecorder) DeleteDynamicResource(name, gvr, wait
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDynamicResource", reflect.TypeOf((*MockClientInterface)(nil).DeleteDynamicResource), name, gvr, wait)
|
||||
}
|
||||
|
||||
// DeleteJob mocks base method.
|
||||
func (m *MockClientInterface) DeleteJob(jobName string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteJob", jobName)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteJob indicates an expected call of DeleteJob.
|
||||
func (mr *MockClientInterfaceMockRecorder) DeleteJob(jobName interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteJob", reflect.TypeOf((*MockClientInterface)(nil).DeleteJob), jobName)
|
||||
}
|
||||
|
||||
// DeleteNamespace mocks base method.
|
||||
func (m *MockClientInterface) DeleteNamespace(name string, wait bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -314,10 +344,10 @@ func (mr *MockClientInterfaceMockRecorder) GeneratePortForwardReq(podName interf
|
||||
}
|
||||
|
||||
// GetAllPodsInNamespaceMatchingSelector mocks base method.
|
||||
func (m *MockClientInterface) GetAllPodsInNamespaceMatchingSelector(selector, ns string) (*v11.PodList, error) {
|
||||
func (m *MockClientInterface) GetAllPodsInNamespaceMatchingSelector(selector, ns string) (*v12.PodList, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAllPodsInNamespaceMatchingSelector", selector, ns)
|
||||
ret0, _ := ret[0].(*v11.PodList)
|
||||
ret0, _ := ret[0].(*v12.PodList)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -591,11 +621,26 @@ func (mr *MockClientInterfaceMockRecorder) GetGVRFromGVK(gvk interface{}) *gomoc
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGVRFromGVK", reflect.TypeOf((*MockClientInterface)(nil).GetGVRFromGVK), gvk)
|
||||
}
|
||||
|
||||
// GetJobLogs mocks base method.
|
||||
func (m *MockClientInterface) GetJobLogs(job *v11.Job, containerName string) (io.ReadCloser, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetJobLogs", job, containerName)
|
||||
ret0, _ := ret[0].(io.ReadCloser)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetJobLogs indicates an expected call of GetJobLogs.
|
||||
func (mr *MockClientInterfaceMockRecorder) GetJobLogs(job, containerName interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetJobLogs", reflect.TypeOf((*MockClientInterface)(nil).GetJobLogs), job, containerName)
|
||||
}
|
||||
|
||||
// GetNamespace mocks base method.
|
||||
func (m *MockClientInterface) GetNamespace(name string) (*v11.Namespace, error) {
|
||||
func (m *MockClientInterface) GetNamespace(name string) (*v12.Namespace, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetNamespace", name)
|
||||
ret0, _ := ret[0].(*v11.Namespace)
|
||||
ret0, _ := ret[0].(*v12.Namespace)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -607,10 +652,10 @@ func (mr *MockClientInterfaceMockRecorder) GetNamespace(name interface{}) *gomoc
|
||||
}
|
||||
|
||||
// GetNamespaceNormal mocks base method.
|
||||
func (m *MockClientInterface) GetNamespaceNormal(name string) (*v11.Namespace, error) {
|
||||
func (m *MockClientInterface) GetNamespaceNormal(name string) (*v12.Namespace, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetNamespaceNormal", name)
|
||||
ret0, _ := ret[0].(*v11.Namespace)
|
||||
ret0, _ := ret[0].(*v12.Namespace)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -682,10 +727,10 @@ func (mr *MockClientInterfaceMockRecorder) GetOneDeploymentFromSelector(selector
|
||||
}
|
||||
|
||||
// GetOneService mocks base method.
|
||||
func (m *MockClientInterface) GetOneService(componentName, appName string, isPartOfComponent bool) (*v11.Service, error) {
|
||||
func (m *MockClientInterface) GetOneService(componentName, appName string, isPartOfComponent bool) (*v12.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetOneService", componentName, appName, isPartOfComponent)
|
||||
ret0, _ := ret[0].(*v11.Service)
|
||||
ret0, _ := ret[0].(*v12.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -697,10 +742,10 @@ func (mr *MockClientInterfaceMockRecorder) GetOneService(componentName, appName,
|
||||
}
|
||||
|
||||
// GetOneServiceFromSelector mocks base method.
|
||||
func (m *MockClientInterface) GetOneServiceFromSelector(selector string) (*v11.Service, error) {
|
||||
func (m *MockClientInterface) GetOneServiceFromSelector(selector string) (*v12.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetOneServiceFromSelector", selector)
|
||||
ret0, _ := ret[0].(*v11.Service)
|
||||
ret0, _ := ret[0].(*v12.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -727,10 +772,10 @@ func (mr *MockClientInterfaceMockRecorder) GetOperatorGVRList() *gomock.Call {
|
||||
}
|
||||
|
||||
// GetPVCFromName mocks base method.
|
||||
func (m *MockClientInterface) GetPVCFromName(pvcName string) (*v11.PersistentVolumeClaim, error) {
|
||||
func (m *MockClientInterface) GetPVCFromName(pvcName string) (*v12.PersistentVolumeClaim, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetPVCFromName", pvcName)
|
||||
ret0, _ := ret[0].(*v11.PersistentVolumeClaim)
|
||||
ret0, _ := ret[0].(*v12.PersistentVolumeClaim)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -757,10 +802,10 @@ func (mr *MockClientInterfaceMockRecorder) GetPodLogs(podName, containerName, fo
|
||||
}
|
||||
|
||||
// GetPodUsingComponentName mocks base method.
|
||||
func (m *MockClientInterface) GetPodUsingComponentName(componentName string) (*v11.Pod, error) {
|
||||
func (m *MockClientInterface) GetPodUsingComponentName(componentName string) (*v12.Pod, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetPodUsingComponentName", componentName)
|
||||
ret0, _ := ret[0].(*v11.Pod)
|
||||
ret0, _ := ret[0].(*v12.Pod)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -772,10 +817,10 @@ func (mr *MockClientInterfaceMockRecorder) GetPodUsingComponentName(componentNam
|
||||
}
|
||||
|
||||
// GetPodsMatchingSelector mocks base method.
|
||||
func (m *MockClientInterface) GetPodsMatchingSelector(selector string) (*v11.PodList, error) {
|
||||
func (m *MockClientInterface) GetPodsMatchingSelector(selector string) (*v12.PodList, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetPodsMatchingSelector", selector)
|
||||
ret0, _ := ret[0].(*v11.PodList)
|
||||
ret0, _ := ret[0].(*v12.PodList)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -847,10 +892,10 @@ func (mr *MockClientInterfaceMockRecorder) GetRestMappingFromUnstructured(arg0 i
|
||||
}
|
||||
|
||||
// GetRunningPodFromSelector mocks base method.
|
||||
func (m *MockClientInterface) GetRunningPodFromSelector(selector string) (*v11.Pod, error) {
|
||||
func (m *MockClientInterface) GetRunningPodFromSelector(selector string) (*v12.Pod, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetRunningPodFromSelector", selector)
|
||||
ret0, _ := ret[0].(*v11.Pod)
|
||||
ret0, _ := ret[0].(*v12.Pod)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -862,10 +907,10 @@ func (mr *MockClientInterfaceMockRecorder) GetRunningPodFromSelector(selector in
|
||||
}
|
||||
|
||||
// GetSecret mocks base method.
|
||||
func (m *MockClientInterface) GetSecret(name, namespace string) (*v11.Secret, error) {
|
||||
func (m *MockClientInterface) GetSecret(name, namespace string) (*v12.Secret, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetSecret", name, namespace)
|
||||
ret0, _ := ret[0].(*v11.Secret)
|
||||
ret0, _ := ret[0].(*v12.Secret)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -1057,10 +1102,10 @@ func (mr *MockClientInterfaceMockRecorder) ListDynamicResources(namespace, gvr,
|
||||
}
|
||||
|
||||
// ListIngresses mocks base method.
|
||||
func (m *MockClientInterface) ListIngresses(namespace, selector string) (*v12.IngressList, error) {
|
||||
func (m *MockClientInterface) ListIngresses(namespace, selector string) (*v13.IngressList, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListIngresses", namespace, selector)
|
||||
ret0, _ := ret[0].(*v12.IngressList)
|
||||
ret0, _ := ret[0].(*v13.IngressList)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -1071,6 +1116,21 @@ func (mr *MockClientInterfaceMockRecorder) ListIngresses(namespace, selector int
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListIngresses", reflect.TypeOf((*MockClientInterface)(nil).ListIngresses), namespace, selector)
|
||||
}
|
||||
|
||||
// ListJobs mocks base method.
|
||||
func (m *MockClientInterface) ListJobs(selector string) (*v11.JobList, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListJobs", selector)
|
||||
ret0, _ := ret[0].(*v11.JobList)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ListJobs indicates an expected call of ListJobs.
|
||||
func (mr *MockClientInterfaceMockRecorder) ListJobs(selector interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListJobs", reflect.TypeOf((*MockClientInterface)(nil).ListJobs), selector)
|
||||
}
|
||||
|
||||
// ListPVCNames mocks base method.
|
||||
func (m *MockClientInterface) ListPVCNames(selector string) ([]string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1087,10 +1147,10 @@ func (mr *MockClientInterfaceMockRecorder) ListPVCNames(selector interface{}) *g
|
||||
}
|
||||
|
||||
// ListPVCs mocks base method.
|
||||
func (m *MockClientInterface) ListPVCs(selector string) ([]v11.PersistentVolumeClaim, error) {
|
||||
func (m *MockClientInterface) ListPVCs(selector string) ([]v12.PersistentVolumeClaim, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListPVCs", selector)
|
||||
ret0, _ := ret[0].([]v11.PersistentVolumeClaim)
|
||||
ret0, _ := ret[0].([]v12.PersistentVolumeClaim)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -1117,10 +1177,10 @@ func (mr *MockClientInterfaceMockRecorder) ListProjectNames() *gomock.Call {
|
||||
}
|
||||
|
||||
// ListSecrets mocks base method.
|
||||
func (m *MockClientInterface) ListSecrets(labelSelector string) ([]v11.Secret, error) {
|
||||
func (m *MockClientInterface) ListSecrets(labelSelector string) ([]v12.Secret, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListSecrets", labelSelector)
|
||||
ret0, _ := ret[0].([]v11.Secret)
|
||||
ret0, _ := ret[0].([]v12.Secret)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -1148,10 +1208,10 @@ func (mr *MockClientInterfaceMockRecorder) ListServiceBindingsFromAllGroups() *g
|
||||
}
|
||||
|
||||
// ListServices mocks base method.
|
||||
func (m *MockClientInterface) ListServices(selector string) ([]v11.Service, error) {
|
||||
func (m *MockClientInterface) ListServices(selector string) ([]v12.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListServices", selector)
|
||||
ret0, _ := ret[0].([]v11.Service)
|
||||
ret0, _ := ret[0].([]v12.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -1291,7 +1351,7 @@ func (mr *MockClientInterfaceMockRecorder) SetNamespace(ns interface{}) *gomock.
|
||||
}
|
||||
|
||||
// SetupPortForwarding mocks base method.
|
||||
func (m *MockClientInterface) SetupPortForwarding(pod *v11.Pod, portPairs []string, out, errOut io.Writer, stopChan chan struct{}) error {
|
||||
func (m *MockClientInterface) SetupPortForwarding(pod *v12.Pod, portPairs []string, out, errOut io.Writer, stopChan chan struct{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetupPortForwarding", pod, portPairs, out, errOut, stopChan)
|
||||
ret0, _ := ret[0].(error)
|
||||
@@ -1305,7 +1365,7 @@ func (mr *MockClientInterfaceMockRecorder) SetupPortForwarding(pod, portPairs, o
|
||||
}
|
||||
|
||||
// TryWithBlockOwnerDeletion mocks base method.
|
||||
func (m *MockClientInterface) TryWithBlockOwnerDeletion(ownerReference v13.OwnerReference, exec func(v13.OwnerReference) error) error {
|
||||
func (m *MockClientInterface) TryWithBlockOwnerDeletion(ownerReference v14.OwnerReference, exec func(v14.OwnerReference) error) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "TryWithBlockOwnerDeletion", ownerReference, exec)
|
||||
ret0, _ := ret[0].(error)
|
||||
@@ -1348,7 +1408,7 @@ func (mr *MockClientInterfaceMockRecorder) UpdateDynamicResource(gvr, name, u in
|
||||
}
|
||||
|
||||
// UpdatePVCLabels mocks base method.
|
||||
func (m *MockClientInterface) UpdatePVCLabels(pvc *v11.PersistentVolumeClaim, labels map[string]string) error {
|
||||
func (m *MockClientInterface) UpdatePVCLabels(pvc *v12.PersistentVolumeClaim, labels map[string]string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdatePVCLabels", pvc, labels)
|
||||
ret0, _ := ret[0].(error)
|
||||
@@ -1362,10 +1422,10 @@ func (mr *MockClientInterfaceMockRecorder) UpdatePVCLabels(pvc, labels interface
|
||||
}
|
||||
|
||||
// UpdateSecret mocks base method.
|
||||
func (m *MockClientInterface) UpdateSecret(secret *v11.Secret, namespace string) (*v11.Secret, error) {
|
||||
func (m *MockClientInterface) UpdateSecret(secret *v12.Secret, namespace string) (*v12.Secret, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateSecret", secret, namespace)
|
||||
ret0, _ := ret[0].(*v11.Secret)
|
||||
ret0, _ := ret[0].(*v12.Secret)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -1377,10 +1437,10 @@ func (mr *MockClientInterfaceMockRecorder) UpdateSecret(secret, namespace interf
|
||||
}
|
||||
|
||||
// UpdateService mocks base method.
|
||||
func (m *MockClientInterface) UpdateService(svc v11.Service) (*v11.Service, error) {
|
||||
func (m *MockClientInterface) UpdateService(svc v12.Service) (*v12.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateService", svc)
|
||||
ret0, _ := ret[0].(*v11.Service)
|
||||
ret0, _ := ret[0].(*v12.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -1392,7 +1452,7 @@ func (mr *MockClientInterfaceMockRecorder) UpdateService(svc interface{}) *gomoc
|
||||
}
|
||||
|
||||
// UpdateStorageOwnerReference mocks base method.
|
||||
func (m *MockClientInterface) UpdateStorageOwnerReference(pvc *v11.PersistentVolumeClaim, ownerReference ...v13.OwnerReference) error {
|
||||
func (m *MockClientInterface) UpdateStorageOwnerReference(pvc *v12.PersistentVolumeClaim, ownerReference ...v14.OwnerReference) error {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []interface{}{pvc}
|
||||
for _, a := range ownerReference {
|
||||
@@ -1411,10 +1471,10 @@ func (mr *MockClientInterfaceMockRecorder) UpdateStorageOwnerReference(pvc inter
|
||||
}
|
||||
|
||||
// WaitAndGetSecret mocks base method.
|
||||
func (m *MockClientInterface) WaitAndGetSecret(name, namespace string) (*v11.Secret, error) {
|
||||
func (m *MockClientInterface) WaitAndGetSecret(name, namespace string) (*v12.Secret, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "WaitAndGetSecret", name, namespace)
|
||||
ret0, _ := ret[0].(*v11.Secret)
|
||||
ret0, _ := ret[0].(*v12.Secret)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -1425,6 +1485,21 @@ func (mr *MockClientInterfaceMockRecorder) WaitAndGetSecret(name, namespace inte
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitAndGetSecret", reflect.TypeOf((*MockClientInterface)(nil).WaitAndGetSecret), name, namespace)
|
||||
}
|
||||
|
||||
// WaitForJobToComplete mocks base method.
|
||||
func (m *MockClientInterface) WaitForJobToComplete(job *v11.Job) (*v11.Job, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "WaitForJobToComplete", job)
|
||||
ret0, _ := ret[0].(*v11.Job)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// WaitForJobToComplete indicates an expected call of WaitForJobToComplete.
|
||||
func (mr *MockClientInterfaceMockRecorder) WaitForJobToComplete(job interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForJobToComplete", reflect.TypeOf((*MockClientInterface)(nil).WaitForJobToComplete), job)
|
||||
}
|
||||
|
||||
// WaitForServiceAccountInNamespace mocks base method.
|
||||
func (m *MockClientInterface) WaitForServiceAccountInNamespace(namespace, serviceAccountName string) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
40
pkg/libdevfile/generator/generators.go
Normal file
40
pkg/libdevfile/generator/generators.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type JobParams struct {
|
||||
TypeMeta metav1.TypeMeta
|
||||
ObjectMeta metav1.ObjectMeta
|
||||
PodTemplateSpec corev1.PodTemplateSpec
|
||||
SpecParams JobSpecParams
|
||||
}
|
||||
type JobSpecParams struct {
|
||||
CompletionMode *batchv1.CompletionMode
|
||||
TTLSecondsAfterFinished *int32
|
||||
BackOffLimit *int32
|
||||
Parallelism *int32
|
||||
Completion *int32
|
||||
ActiveDeadlineSeconds *int64
|
||||
}
|
||||
|
||||
func GetJob(jobParams JobParams) batchv1.Job {
|
||||
return batchv1.Job{
|
||||
TypeMeta: jobParams.TypeMeta,
|
||||
ObjectMeta: jobParams.ObjectMeta,
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: jobParams.PodTemplateSpec,
|
||||
Parallelism: jobParams.SpecParams.Parallelism,
|
||||
Completions: jobParams.SpecParams.Completion,
|
||||
ActiveDeadlineSeconds: jobParams.SpecParams.ActiveDeadlineSeconds,
|
||||
BackoffLimit: jobParams.SpecParams.BackOffLimit,
|
||||
// we delete jobs before exiting this function but setting this as a backup in case DeleteJob fails
|
||||
TTLSecondsAfterFinished: jobParams.SpecParams.TTLSecondsAfterFinished,
|
||||
CompletionMode: jobParams.SpecParams.CompletionMode,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
@@ -139,7 +139,11 @@ func (o *ComponentOptions) Run(ctx context.Context) error {
|
||||
if o.name != "" {
|
||||
return o.deleteNamedComponent(ctx)
|
||||
}
|
||||
return o.deleteDevfileComponent(ctx)
|
||||
remainingResources, err := o.deleteDevfileComponent(ctx)
|
||||
if err == nil {
|
||||
printRemainingResources(ctx, remainingResources)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// deleteNamedComponent deletes a component given its name
|
||||
@@ -226,9 +230,23 @@ func messageWithPlatforms(cluster, podman bool, name, namespace string) string {
|
||||
return fmt.Sprintf("No resource found for component %q%s\n", name, strings.Join(details, " or"))
|
||||
}
|
||||
|
||||
// printRemainingResources lists the remaining cluster resources that are not found in the devfile.
|
||||
func printRemainingResources(ctx context.Context, remainingResources []unstructured.Unstructured) {
|
||||
if len(remainingResources) == 0 {
|
||||
return
|
||||
}
|
||||
componentName := odocontext.GetComponentName(ctx)
|
||||
namespace := odocontext.GetNamespace(ctx)
|
||||
log.Printf("There are still resources left in the cluster that might be belonging to the deleted component.")
|
||||
for _, resource := range remainingResources {
|
||||
fmt.Printf("\t- %s: %s\n", resource.GetKind(), resource.GetName())
|
||||
}
|
||||
log.Infof("If you want to delete those, execute `odo delete component --name %s --namespace %s`\n", componentName, namespace)
|
||||
}
|
||||
|
||||
// deleteDevfileComponent deletes all the components defined by the devfile in the current directory
|
||||
// devfileObj in context must not be nil when this method is called
|
||||
func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) ([]unstructured.Unstructured, error) {
|
||||
var (
|
||||
devfileObj = odocontext.GetDevfileObj(ctx)
|
||||
componentName = odocontext.GetComponentName(ctx)
|
||||
@@ -238,6 +256,7 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
isClusterInnerLoopDeployed bool
|
||||
hasClusterResources bool
|
||||
clusterResources []unstructured.Unstructured
|
||||
remainingResources []unstructured.Unstructured
|
||||
|
||||
isPodmanInnerLoopDeployed bool
|
||||
hasPodmanResources bool
|
||||
@@ -247,6 +266,7 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
)
|
||||
|
||||
log.Info("Searching resources to delete, please wait...")
|
||||
|
||||
if o.clientset.KubernetesClient != nil {
|
||||
isClusterInnerLoopDeployed, clusterResources, err = o.clientset.DeleteClient.ListClusterResourcesToDeleteFromDevfile(
|
||||
*devfileObj, appName, componentName, o.runningIn)
|
||||
@@ -254,21 +274,26 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
if clierrors.AsWarning(err) {
|
||||
log.Warning(err.Error())
|
||||
} else {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
namespace = odocontext.GetNamespace(ctx)
|
||||
hasClusterResources = len(clusterResources) != 0
|
||||
// Get a list of component's resources present on the cluster
|
||||
deployedResources, _ := o.clientset.DeleteClient.ListClusterResourcesToDelete(ctx, componentName, namespace, o.runningIn)
|
||||
// Get a list of component's resources absent from the devfile, but present on the cluster
|
||||
remainingResources = listResourcesMissingFromDevfilePresentOnCluster(componentName, clusterResources, deployedResources)
|
||||
}
|
||||
|
||||
// 2. get podman resources
|
||||
if o.clientset.PodmanClient != nil {
|
||||
isPodmanInnerLoopDeployed, podmanPods, err = o.clientset.DeleteClient.ListPodmanResourcesToDelete(appName, componentName, o.runningIn)
|
||||
if err != nil {
|
||||
if clierrors.AsWarning(err) {
|
||||
log.Warning(err.Error())
|
||||
} else {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
hasPodmanResources = len(podmanPods) != 0
|
||||
@@ -277,7 +302,8 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
if !(hasClusterResources || hasPodmanResources) {
|
||||
log.Infof(messageWithPlatforms(o.clientset.KubernetesClient != nil, o.clientset.PodmanClient != nil, componentName, namespace))
|
||||
if !o.withFilesFlag {
|
||||
return nil
|
||||
// check for resources here
|
||||
return remainingResources, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +313,7 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
if o.withFilesFlag {
|
||||
filesToDelete, err = getFilesCreatedByOdo(o.clientset.FS, ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
printFileCreatedByOdo(filesToDelete, hasClusterResources)
|
||||
}
|
||||
@@ -295,7 +321,7 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
|
||||
if !(hasClusterResources || hasPodmanResources || hasFilesToDelete) {
|
||||
klog.V(2).Info("no cluster resources and no files to delete")
|
||||
return nil
|
||||
return remainingResources, nil
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Are you sure you want to delete %q and all its resources?", componentName)
|
||||
@@ -306,10 +332,6 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
|
||||
if hasClusterResources {
|
||||
spinner := log.Spinnerf("Deleting resources from cluster")
|
||||
// Get a list of component's resources present on the cluster
|
||||
deployedResources, _ := o.clientset.DeleteClient.ListClusterResourcesToDelete(ctx, componentName, namespace, o.runningIn)
|
||||
// Get a list of component's resources absent from the devfile, but present on the cluster
|
||||
remainingResources := listResourcesMissingFromDevfilePresentOnCluster(componentName, clusterResources, deployedResources)
|
||||
|
||||
// if innerloop deployment resource is present, then execute preStop events
|
||||
if isClusterInnerLoopDeployed {
|
||||
@@ -328,13 +350,6 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
spinner.End(true)
|
||||
log.Infof("The component %q is successfully deleted from namespace %q\n", componentName, namespace)
|
||||
|
||||
if len(remainingResources) != 0 {
|
||||
log.Printf("There are still resources left in the cluster that might be belonging to the deleted component.")
|
||||
for _, resource := range remainingResources {
|
||||
fmt.Printf("\t- %s: %s\n", resource.GetKind(), resource.GetName())
|
||||
}
|
||||
log.Infof("If you want to delete those, execute `odo delete component --name %s --namespace %s`\n", componentName, namespace)
|
||||
}
|
||||
}
|
||||
|
||||
if hasPodmanResources {
|
||||
@@ -354,7 +369,7 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if o.withFilesFlag {
|
||||
//Delete files
|
||||
// Delete files
|
||||
remainingFiles := o.deleteFilesCreatedByOdo(o.clientset.FS, filesToDelete)
|
||||
var listOfFiles []string
|
||||
for f, e := range remainingFiles {
|
||||
@@ -367,12 +382,11 @@ func (o *ComponentOptions) deleteDevfileComponent(ctx context.Context) error {
|
||||
log.Info("You need to manually delete those.")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return remainingResources, nil
|
||||
}
|
||||
|
||||
log.Error("Aborting deletion of component")
|
||||
|
||||
return nil
|
||||
return remainingResources, nil
|
||||
}
|
||||
|
||||
// listResourcesMissingFromDevfilePresentOnCluster returns a list of resources belonging to a component name that are present on cluster, but missing from devfile
|
||||
|
||||
@@ -413,10 +413,11 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
runningIn string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
deleteClient func(ctrl *gomock.Controller) _delete.Client
|
||||
name string
|
||||
fields fields
|
||||
remainingResources []unstructured.Unstructured
|
||||
wantErr bool
|
||||
deleteClient func(ctrl *gomock.Controller) _delete.Client
|
||||
}{
|
||||
{
|
||||
name: "deleting a component with access to devfile",
|
||||
@@ -471,6 +472,23 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "deleting a component running in Deploy with access to devfile, with no resource present in the devfile but some present on the cluster",
|
||||
deleteClient: func(ctrl *gomock.Controller) _delete.Client {
|
||||
deleteClient := _delete.NewMockClient(ctrl)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentDeployMode).
|
||||
Return(true, nil, nil)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), compName, projectName, labels.ComponentDeployMode).
|
||||
Return(resources, nil)
|
||||
return deleteClient
|
||||
},
|
||||
fields: fields{
|
||||
forceFlag: true,
|
||||
runningIn: labels.ComponentDeployMode,
|
||||
},
|
||||
wantErr: false,
|
||||
remainingResources: resources,
|
||||
},
|
||||
{
|
||||
name: "deleting a component should not fail even if ExecutePreStopEvents fails",
|
||||
deleteClient: func(ctrl *gomock.Controller) _delete.Client {
|
||||
@@ -529,6 +547,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient: func(ctrl *gomock.Controller) _delete.Client {
|
||||
deleteClient := _delete.NewMockClient(ctrl)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentAnyMode).Return(true, resources, nil)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentAnyMode)
|
||||
return deleteClient
|
||||
},
|
||||
fields: fields{
|
||||
@@ -541,6 +560,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient: func(ctrl *gomock.Controller) _delete.Client {
|
||||
deleteClient := _delete.NewMockClient(ctrl)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentDevMode).Return(true, resources, nil)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDevMode)
|
||||
return deleteClient
|
||||
},
|
||||
fields: fields{
|
||||
@@ -554,6 +574,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient: func(ctrl *gomock.Controller) _delete.Client {
|
||||
deleteClient := _delete.NewMockClient(ctrl)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentDeployMode).Return(true, resources, nil)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDeployMode)
|
||||
return deleteClient
|
||||
},
|
||||
fields: fields{
|
||||
@@ -568,6 +589,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient := _delete.NewMockClient(ctrl)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentAnyMode).
|
||||
Return(false, nil, nil)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentAnyMode)
|
||||
return deleteClient
|
||||
},
|
||||
fields: fields{
|
||||
@@ -581,6 +603,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient := _delete.NewMockClient(ctrl)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentDevMode).
|
||||
Return(false, nil, nil)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDevMode)
|
||||
return deleteClient
|
||||
},
|
||||
fields: fields{
|
||||
@@ -595,6 +618,7 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
deleteClient := _delete.NewMockClient(ctrl)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDeleteFromDevfile(gomock.Any(), appName, gomock.Any(), labels.ComponentDeployMode).
|
||||
Return(false, nil, nil)
|
||||
deleteClient.EXPECT().ListClusterResourcesToDelete(gomock.Any(), gomock.Any(), gomock.Any(), labels.ComponentDeployMode)
|
||||
return deleteClient
|
||||
},
|
||||
fields: fields{
|
||||
@@ -628,9 +652,13 @@ func TestComponentOptions_deleteDevfileComponent(t *testing.T) {
|
||||
ctx = odocontext.WithWorkingDirectory(ctx, workingDir)
|
||||
ctx = odocontext.WithComponentName(ctx, compName)
|
||||
ctx = odocontext.WithDevfileObj(ctx, &info)
|
||||
if err = o.deleteDevfileComponent(ctx); (err != nil) != tt.wantErr {
|
||||
remainingResources, err := o.deleteDevfileComponent(ctx)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("deleteDevfileComponent() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if diff := cmp.Diff(remainingResources, tt.remainingResources); diff != "" {
|
||||
t.Errorf("deleteDevfileComponent() did not return expected resources: %s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package deploy
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
dfutil "github.com/devfile/library/v2/pkg/util"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
@@ -62,7 +63,9 @@ func (o *DeployOptions) Validate(ctx context.Context) error {
|
||||
if devfileObj == nil {
|
||||
return genericclioptions.NewNoDevfileError(odocontext.GetWorkingDirectory(ctx))
|
||||
}
|
||||
return nil
|
||||
componentName := odocontext.GetComponentName(ctx)
|
||||
err := dfutil.ValidateK8sResourceName("component name", componentName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo command
|
||||
|
||||
@@ -104,10 +104,7 @@ func NamespaceKubernetesObject(componentName string, applicationName string) (st
|
||||
// NamespaceKubernetesObjectWithTrim hyphenates applicationName and componentName
|
||||
// if the resultant name is greater than 63 characters
|
||||
// it trims app name then component name
|
||||
func NamespaceKubernetesObjectWithTrim(componentName, applicationName string) (string, error) {
|
||||
var (
|
||||
maxLen = 63
|
||||
)
|
||||
func NamespaceKubernetesObjectWithTrim(componentName, applicationName string, maxLen int) (string, error) {
|
||||
value, err := NamespaceKubernetesObject(componentName, applicationName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -2564,7 +2564,7 @@ func TestNamespaceKubernetesObjectWithTrim(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NamespaceKubernetesObjectWithTrim(tt.args.componentName, tt.args.applicationName)
|
||||
got, err := NamespaceKubernetesObjectWithTrim(tt.args.componentName, tt.args.applicationName, 63)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NamespaceKubernetesObjectWithTrim() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
commands:
|
||||
- exec:
|
||||
commandLine: npm install
|
||||
component: runtime
|
||||
group:
|
||||
isDefault: true
|
||||
kind: build
|
||||
workingDir: /project
|
||||
id: install
|
||||
- exec:
|
||||
commandLine: npm start
|
||||
component: runtime
|
||||
group:
|
||||
isDefault: true
|
||||
kind: run
|
||||
workingDir: /project
|
||||
id: run
|
||||
- exec:
|
||||
commandLine: npm run debug
|
||||
component: runtime
|
||||
group:
|
||||
isDefault: true
|
||||
kind: debug
|
||||
workingDir: /project
|
||||
id: debug
|
||||
- exec:
|
||||
commandLine: npm test
|
||||
component: runtime
|
||||
group:
|
||||
isDefault: true
|
||||
kind: test
|
||||
workingDir: /project
|
||||
id: test
|
||||
- exec:
|
||||
commandLine: echo Hello world
|
||||
component: runtime
|
||||
id: deploy-exec
|
||||
- id: deploy
|
||||
composite:
|
||||
commands:
|
||||
- deploy-exec
|
||||
group:
|
||||
kind: deploy
|
||||
isDefault: true
|
||||
components:
|
||||
- container:
|
||||
endpoints:
|
||||
- name: http-3000
|
||||
targetPort: 3000
|
||||
image: registry.access.redhat.com/ubi8/nodejs-14:latest
|
||||
memoryLimit: 1024Mi
|
||||
mountSources: true
|
||||
sourceMapping: /project
|
||||
name: runtime
|
||||
metadata:
|
||||
description: Stack with Node.js 14
|
||||
displayName: Node.js Runtime
|
||||
icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg
|
||||
language: javascript
|
||||
name: nodejs-prj1-api-abhz
|
||||
projectType: nodejs
|
||||
tags:
|
||||
- NodeJS
|
||||
- Express
|
||||
- ubi8
|
||||
version: 1.0.1
|
||||
schemaVersion: 2.2.0
|
||||
starterProjects:
|
||||
- git:
|
||||
remotes:
|
||||
origin: https://github.com/odo-devfiles/nodejs-ex.git
|
||||
name: nodejs-starter
|
||||
variables:
|
||||
CONTAINER_IMAGE: quay.io/unknown-account/myimage
|
||||
@@ -132,10 +132,15 @@ var _ = Describe("odo delete command tests", func() {
|
||||
title string
|
||||
devfileName string
|
||||
setupFunc func()
|
||||
// TODO(pvala): Find a better solution to renaming a resource when the data is in a different location
|
||||
renameServiceFunc func(newName string)
|
||||
}{
|
||||
{
|
||||
title: "a component is bootstrapped",
|
||||
devfileName: "devfile-deploy-with-multiple-resources.yaml",
|
||||
renameServiceFunc: func(newName string) {
|
||||
helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), fmt.Sprintf("name: %s", serviceName), fmt.Sprintf("name: %s", newName))
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "a component is bootstrapped using a devfile.yaml with URI-referenced Kubernetes components",
|
||||
@@ -145,6 +150,9 @@ var _ = Describe("odo delete command tests", func() {
|
||||
filepath.Join("source", "devfiles", "nodejs", "kubernetes", "devfile-deploy-with-multiple-resources-and-k8s-uri"),
|
||||
filepath.Join(commonVar.Context, "kubernetes", "devfile-deploy-with-multiple-resources-and-k8s-uri"))
|
||||
},
|
||||
renameServiceFunc: func(newName string) {
|
||||
helper.ReplaceString(filepath.Join(commonVar.Context, "kubernetes", "devfile-deploy-with-multiple-resources-and-k8s-uri", "outerloop-deploy-2.yaml"), fmt.Sprintf("name: %s", serviceName), fmt.Sprintf("name: %s", newName))
|
||||
},
|
||||
},
|
||||
} {
|
||||
// this is a workaround to ensure that the for loop works with `It` blocks
|
||||
@@ -451,12 +459,12 @@ var _ = Describe("odo delete command tests", func() {
|
||||
|
||||
for _, withFiles := range []bool{true, false} {
|
||||
withFiles := withFiles
|
||||
When(fmt.Sprintf("a resource is changed in the devfile and the component is deleted while having access to the devfile.yaml with --files=%v",
|
||||
withFiles), func() {
|
||||
When(fmt.Sprintf("a resource is changed in the devfile and the component is deleted while having access to the devfile.yaml with --files=%v --running-in=%v",
|
||||
withFiles, runningIn), func() {
|
||||
var changedServiceName, stdout string
|
||||
BeforeEach(func() {
|
||||
changedServiceName = "my-changed-cs"
|
||||
helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), fmt.Sprintf("name: %s", serviceName), fmt.Sprintf("name: %s", changedServiceName))
|
||||
ctx.renameServiceFunc(changedServiceName)
|
||||
|
||||
args := []string{"delete", "component"}
|
||||
if withFiles {
|
||||
@@ -477,7 +485,7 @@ var _ = Describe("odo delete command tests", func() {
|
||||
Expect(stdout).To(SatisfyAll(
|
||||
ContainSubstring("There are still resources left in the cluster that might be belonging to the deleted component"),
|
||||
Not(ContainSubstring(changedServiceName)),
|
||||
ContainSubstring(serviceName),
|
||||
ContainSubstring(fmt.Sprintf("Service: %s", serviceName)),
|
||||
ContainSubstring("odo delete component --name %s --namespace %s", cmpName, commonVar.Project),
|
||||
))
|
||||
})
|
||||
@@ -729,4 +737,21 @@ var _ = Describe("odo delete command tests", func() {
|
||||
})
|
||||
})
|
||||
}
|
||||
When("running odo deploy for an exec command bound to fail", func() {
|
||||
BeforeEach(func() {
|
||||
helper.CopyExampleDevFile(
|
||||
filepath.Join("source", "devfiles", "nodejs", "devfile-deploy-exec.yaml"),
|
||||
path.Join(commonVar.Context, "devfile.yaml"),
|
||||
helper.DevfileMetadataNameSetter(cmpName))
|
||||
helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), `image: registry.access.redhat.com/ubi8/nodejs-14:latest`, `image: registry.access.redhat.com/ubi8/nodejs-does-not-exist-14:latest`)
|
||||
// We terminate after 5 seconds because the job should have been created by then and is bound to fail.
|
||||
helper.Cmd("odo", "deploy").WithTerminate(5, nil).ShouldRun()
|
||||
})
|
||||
It("should print the job in the list of resources to be deleted with named delete command", func() {
|
||||
out := helper.Cmd("odo", "delete", "component", "-f").ShouldPass().Out()
|
||||
Expect(out).To(SatisfyAll(
|
||||
ContainSubstring("There are still resources left in the cluster that might be belonging to the deleted component."),
|
||||
ContainSubstring(fmt.Sprintf("Job: %s-app-deploy-exec", cmpName))))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,11 +9,9 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
segment "github.com/redhat-developer/odo/pkg/segment/context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
segment "github.com/redhat-developer/odo/pkg/segment/context"
|
||||
"github.com/redhat-developer/odo/tests/helper"
|
||||
)
|
||||
|
||||
@@ -483,6 +481,101 @@ CMD ["npm", "start"]
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
Context("deploying devfile with exec", func() {
|
||||
BeforeEach(func() {
|
||||
helper.CopyExampleDevFile(
|
||||
filepath.Join("source", "devfiles", "nodejs", "devfile-deploy-exec.yaml"),
|
||||
path.Join(commonVar.Context, "devfile.yaml"),
|
||||
helper.DevfileMetadataNameSetter(cmpName))
|
||||
})
|
||||
for _, ctx := range []struct {
|
||||
title, compName string
|
||||
}{
|
||||
{
|
||||
title: "component name of at max(63) characters length",
|
||||
compName: "document-how-odo-translates-container-component-to-deploymentss",
|
||||
},
|
||||
{
|
||||
title: "component name of a normal character length",
|
||||
compName: helper.RandString(6),
|
||||
},
|
||||
} {
|
||||
ctx := ctx
|
||||
When(fmt.Sprintf("using devfile that works; with %s", ctx.title), func() {
|
||||
BeforeEach(func() {
|
||||
helper.UpdateDevfileContent(filepath.Join(commonVar.Context, "devfile.yaml"), []helper.DevfileUpdater{helper.DevfileMetadataNameSetter(ctx.compName)})
|
||||
})
|
||||
It("should complete the command execution successfully", func() {
|
||||
out := helper.Cmd("odo", "deploy").ShouldPass().Out()
|
||||
Expect(out).To(ContainSubstring("Executing command in container (command: deploy-exec)"))
|
||||
})
|
||||
})
|
||||
|
||||
// We check the following tests for character length as long as 63 and for normal character length because for 63 char,
|
||||
// the job name will be truncated, and we want to ensure the correct truncated name is used to delete the old job before running a new one so that `odo deploy` does not fail
|
||||
When(fmt.Sprintf("the deploy command terminates abruptly; %s", ctx.title), func() {
|
||||
BeforeEach(func() {
|
||||
helper.UpdateDevfileContent(filepath.Join(commonVar.Context, "devfile.yaml"), []helper.DevfileUpdater{helper.DevfileMetadataNameSetter(ctx.compName)})
|
||||
helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), `image: registry.access.redhat.com/ubi8/nodejs-14:latest`, `image: registry.access.redhat.com/ubi8/nodejs-does-not-exist-14:latest`)
|
||||
helper.Cmd("odo", "deploy").WithTimeout(10).ShouldFail()
|
||||
})
|
||||
When("odo deploy command is run again", func() {
|
||||
BeforeEach(func() {
|
||||
// Restore the Devfile; this is not a required step to test, but we do it to not abruptly terminate the command again
|
||||
helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), `image: registry.access.redhat.com/ubi8/nodejs-does-not-exist-14:latest`, `image: registry.access.redhat.com/ubi8/nodejs-14:latest`)
|
||||
})
|
||||
It("should run successfully", func() {
|
||||
helper.Cmd("odo", "deploy").ShouldPass()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
When("using a devfile name with length more than 63", func() {
|
||||
const (
|
||||
unacceptableLongName = "document-how-odo-translates-container-component-to-deploymentsss"
|
||||
)
|
||||
BeforeEach(func() {
|
||||
helper.UpdateDevfileContent(filepath.Join(commonVar.Context, "devfile.yaml"), []helper.DevfileUpdater{helper.DevfileMetadataNameSetter(unacceptableLongName)})
|
||||
})
|
||||
It("should fail with invalid component name error", func() {
|
||||
errOut := helper.Cmd("odo", "deploy").ShouldFail().Err()
|
||||
Expect(errOut).To(SatisfyAll(ContainSubstring(fmt.Sprintf("component name %q is not valid", unacceptableLongName)),
|
||||
ContainSubstring("Contain at most 63 characters"),
|
||||
ContainSubstring("Start with an alphanumeric character"),
|
||||
ContainSubstring("End with an alphanumeric character"),
|
||||
ContainSubstring("Must not contain all numeric values")))
|
||||
})
|
||||
})
|
||||
|
||||
When("using devfile with a long running command in exec", func() {
|
||||
BeforeEach(func() {
|
||||
helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), `commandLine: echo Hello world`, `commandLine: sleep 62; echo hello world`)
|
||||
})
|
||||
It("should print the tip to run odo logs after 1 minute of execution", func() {
|
||||
out := helper.Cmd("odo", "deploy").ShouldPass().Out()
|
||||
Expect(out).To(ContainSubstring("Tip: Run `odo logs --deploy --follow` to get the logs of the command output."))
|
||||
})
|
||||
})
|
||||
|
||||
When("using devfile where the exec command is bound to fail", func() {
|
||||
BeforeEach(func() {
|
||||
// the following new commandLine ensures "counter $i counter" is printed on 99 lines of the output and the last line is a failure from running a non-existent binary
|
||||
helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), `commandLine: echo Hello world`, `commandLine: for i in {1..100}; do echo counter $i counter; done; run-non-existent-binary`)
|
||||
})
|
||||
|
||||
It("should print the last 100 lines of the log to the output", func() {
|
||||
out, errOut := helper.Cmd("odo", "deploy").ShouldFail().OutAndErr()
|
||||
Expect(out).To(ContainSubstring("Execution output:"))
|
||||
// checking 'counter 1 counter' does not exist in the log output ensures that only the last 100 lines are printed
|
||||
Expect(errOut).ToNot(ContainSubstring("counter 1 counter"))
|
||||
Expect(errOut).To(ContainSubstring("/bin/sh: run-non-existent-binary: command not found"))
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user