diff --git a/pkg/deploy/deploy.go b/pkg/deploy/deploy.go index 0129b497bd6..edaeabd0b35 100644 --- a/pkg/deploy/deploy.go +++ b/pkg/deploy/deploy.go @@ -1,11 +1,9 @@ package deploy import ( - "errors" - "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/devfile/library/pkg/devfile/parser" - + "github.com/devfile/library/pkg/devfile/parser/data/v2/common" "github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/component" "github.com/redhat-developer/odo/pkg/devfile/image" "github.com/redhat-developer/odo/pkg/kclient" @@ -69,5 +67,41 @@ 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") + + // get the containers matching the command + containers, err := libdevfile.GetContainerComponentsForCommand(o.devfileObj, command) + if err != nil { + return err + } + if len(containers) != 1 { + return libdevfile.NewNotExactlyOneContainer() + } + containerName := containers[0] + + // get all the container components from the devfile and find the one we are interested in + devfileContainers, err := o.devfileObj.Data.GetComponents(common.DevfileOptions{ + ComponentOptions: common.ComponentOptions{v1alpha2.ContainerComponentType}, + }) + if err != nil { + return err + } + var devfileContainer v1alpha2.Component + var found bool + for _, dc := range devfileContainers { + if dc.Name == containerName { + found = true + devfileContainer = dc + } + } + if !found { + return libdevfile.NewNotExactlyOneContainer() + } + + job, err := libdevfile.GetJobFromContainerWithCommand(devfileContainer, command) + if err != nil { + return err + } + job.Labels = odolabels.GetLabels(o.devfileObj.GetMetadataName(), "app", "", odolabels.ComponentDeployMode, false) + + return o.kubeClient.CreateJob(job) } diff --git a/pkg/kclient/interface.go b/pkg/kclient/interface.go index ffbbbb7ff4c..afafe7dce56 100644 --- a/pkg/kclient/interface.go +++ b/pkg/kclient/interface.go @@ -3,6 +3,7 @@ package kclient import ( "context" "io" + batchv1 "k8s.io/api/batch/v1" "time" "github.com/go-openapi/spec" @@ -67,6 +68,9 @@ type ClientInterface interface { // events.go PodWarningEventWatcher(ctx context.Context) (result watch.Interface, isForbidden bool, err error) + // jobs.go + CreateJob(job batchv1.Job) error + // kclient.go GetClient() kubernetes.Interface GetConfig() clientcmd.ClientConfig diff --git a/pkg/kclient/jobs.go b/pkg/kclient/jobs.go new file mode 100644 index 00000000000..025da5003b2 --- /dev/null +++ b/pkg/kclient/jobs.go @@ -0,0 +1,15 @@ +package kclient + +import ( + "context" + batchv1 "k8s.io/api/batch/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (c *Client) CreateJob(job batchv1.Job) error { + _, err := c.KubeClient.BatchV1().Jobs(c.Namespace).Create(context.TODO(), &job, metav1.CreateOptions{}) + if err != nil { + return err + } + return nil +} diff --git a/pkg/kclient/mock_Client.go b/pkg/kclient/mock_Client.go index 38e9e47b06d..a564de840fd 100644 --- a/pkg/kclient/mock_Client.go +++ b/pkg/kclient/mock_Client.go @@ -18,9 +18,10 @@ 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" + v11 "k8s.io/api/batch/v1" + v12 "k8s.io/api/core/v1" meta "k8s.io/apimachinery/pkg/api/meta" - v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + v13 "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" @@ -113,11 +114,25 @@ 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) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateJob", job) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateJob indicates an expected call of CreateJob. +func (mr *MockClientInterfaceMockRecorder) CreateJob(job interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateJob", reflect.TypeOf((*MockClientInterface)(nil).CreateJob), job) +} + // 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 } @@ -143,10 +158,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 } @@ -158,7 +173,7 @@ func (mr *MockClientInterfaceMockRecorder) CreatePVC(pvc interface{}) *gomock.Ca } // CreateSecret mocks base method. -func (m *MockClientInterface) CreateSecret(objectMeta v12.ObjectMeta, data map[string]string, ownerReference v12.OwnerReference) error { +func (m *MockClientInterface) CreateSecret(objectMeta v13.ObjectMeta, data map[string]string, ownerReference v13.OwnerReference) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateSecret", objectMeta, data, ownerReference) ret0, _ := ret[0].(error) @@ -172,7 +187,7 @@ func (mr *MockClientInterfaceMockRecorder) CreateSecret(objectMeta, data, ownerR } // CreateSecrets mocks base method. -func (m *MockClientInterface) CreateSecrets(componentName string, commonObjectMeta v12.ObjectMeta, svc *v11.Service, ownerReference v12.OwnerReference) error { +func (m *MockClientInterface) CreateSecrets(componentName string, commonObjectMeta v13.ObjectMeta, svc *v12.Service, ownerReference v13.OwnerReference) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateSecrets", componentName, commonObjectMeta, svc, ownerReference) ret0, _ := ret[0].(error) @@ -186,10 +201,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 } @@ -201,10 +216,10 @@ func (mr *MockClientInterfaceMockRecorder) CreateService(svc interface{}) *gomoc } // CreateTLSSecret mocks base method. -func (m *MockClientInterface) CreateTLSSecret(tlsCertificate, tlsPrivKey []byte, objectMeta v12.ObjectMeta) (*v11.Secret, error) { +func (m *MockClientInterface) CreateTLSSecret(tlsCertificate, tlsPrivKey []byte, objectMeta v13.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 } @@ -357,10 +372,10 @@ func (mr *MockClientInterfaceMockRecorder) GeneratePortForwardReq(podName interf } // GetAllPodsInNamespace mocks base method. -func (m *MockClientInterface) GetAllPodsInNamespace() (*v11.PodList, error) { +func (m *MockClientInterface) GetAllPodsInNamespace() (*v12.PodList, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllPodsInNamespace") - ret0, _ := ret[0].(*v11.PodList) + ret0, _ := ret[0].(*v12.PodList) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -635,10 +650,10 @@ func (mr *MockClientInterfaceMockRecorder) GetGVRFromGVK(gvk interface{}) *gomoc } // 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 } @@ -650,10 +665,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 } @@ -710,10 +725,10 @@ func (mr *MockClientInterfaceMockRecorder) GetOneDeploymentFromSelector(selector } // GetOneService mocks base method. -func (m *MockClientInterface) GetOneService(componentName, appName string) (*v11.Service, error) { +func (m *MockClientInterface) GetOneService(componentName, appName string) (*v12.Service, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOneService", componentName, appName) - ret0, _ := ret[0].(*v11.Service) + ret0, _ := ret[0].(*v12.Service) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -725,10 +740,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 } @@ -755,10 +770,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 } @@ -785,10 +800,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 } @@ -800,10 +815,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 } @@ -875,10 +890,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 } @@ -890,10 +905,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 } @@ -1100,10 +1115,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 } @@ -1130,10 +1145,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 } @@ -1161,10 +1176,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 } @@ -1289,7 +1304,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) @@ -1303,7 +1318,7 @@ func (mr *MockClientInterfaceMockRecorder) SetupPortForwarding(pod, portPairs, o } // TryWithBlockOwnerDeletion mocks base method. -func (m *MockClientInterface) TryWithBlockOwnerDeletion(ownerReference v12.OwnerReference, exec func(v12.OwnerReference) error) error { +func (m *MockClientInterface) TryWithBlockOwnerDeletion(ownerReference v13.OwnerReference, exec func(v13.OwnerReference) error) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TryWithBlockOwnerDeletion", ownerReference, exec) ret0, _ := ret[0].(error) @@ -1346,7 +1361,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) @@ -1360,10 +1375,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 } @@ -1375,10 +1390,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 } @@ -1390,7 +1405,7 @@ func (mr *MockClientInterfaceMockRecorder) UpdateService(svc interface{}) *gomoc } // UpdateStorageOwnerReference mocks base method. -func (m *MockClientInterface) UpdateStorageOwnerReference(pvc *v11.PersistentVolumeClaim, ownerReference ...v12.OwnerReference) error { +func (m *MockClientInterface) UpdateStorageOwnerReference(pvc *v12.PersistentVolumeClaim, ownerReference ...v13.OwnerReference) error { m.ctrl.T.Helper() varargs := []interface{}{pvc} for _, a := range ownerReference { @@ -1409,10 +1424,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 } diff --git a/pkg/libdevfile/command_composite.go b/pkg/libdevfile/command_composite.go index f01057a4331..69ef78c37d4 100644 --- a/pkg/libdevfile/command_composite.go +++ b/pkg/libdevfile/command_composite.go @@ -29,7 +29,8 @@ func (o *compositeCommand) CheckValidity() error { if err != nil { return err } - cmds := o.command.Composite.Commands + var cmds []string + copy(cmds, o.command.Composite.Commands) for _, cmd := range cmds { if _, ok := allCommands[strings.ToLower(cmd)]; !ok { return fmt.Errorf("composite command %q references command %q not found in devfile", o.command.Id, cmd) diff --git a/pkg/libdevfile/component_container.go b/pkg/libdevfile/component_container.go index eedb627536b..e1afe404595 100644 --- a/pkg/libdevfile/component_container.go +++ b/pkg/libdevfile/component_container.go @@ -3,6 +3,9 @@ package libdevfile import ( "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/devfile/library/pkg/devfile/parser" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" ) // containerComponent implements the component interface @@ -24,6 +27,25 @@ func (e *containerComponent) CheckValidity() error { return nil } -func (e *containerComponent) Apply(handler Handler) error { +func (e *containerComponent) Apply(_ Handler) error { return nil } + +func GetJobFromContainerWithCommand(devfileContainer v1alpha2.Component, command v1alpha2.Command) (batchv1.Job, error) { + var job batchv1.Job + var container corev1.Container + + job.GenerateName = devfileContainer.Name + container.Image = devfileContainer.Container.Image + container.Name = devfileContainer.Name + container.Command = []string{"/bin/sh", "-c", "(" + command.Exec.CommandLine + ") "} + container.ImagePullPolicy = corev1.PullIfNotPresent + + job.Spec.Template.Spec.Containers = []corev1.Container{container} + job.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever + + // delete the job from the cluster after 100 seconds. + job.Spec.TTLSecondsAfterFinished = pointer.Int32Ptr(100) + + return job, nil +} diff --git a/pkg/libdevfile/errors.go b/pkg/libdevfile/errors.go index 491af5d56f8..05435e8c0f8 100644 --- a/pkg/libdevfile/errors.go +++ b/pkg/libdevfile/errors.go @@ -106,3 +106,14 @@ type NoCommandForGroup struct { func (n NoCommandForGroup) Error() string { return fmt.Sprintf("the command group of kind \"%v\" is not found in the devfile", n.Group) } + +// NotExactlyOneContainer is returned when there's more or less than exactly one container component matching a command +type NotExactlyOneContainer struct{} + +func NewNotExactlyOneContainer() NotExactlyOneContainer { + return NotExactlyOneContainer{} +} + +func (_ NotExactlyOneContainer) Error() string { + return fmt.Sprintf("need exactly one container component to run exec command") +} diff --git a/pkg/util/util.go b/pkg/util/util.go index 34625356754..83885727b44 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -746,7 +746,7 @@ func GetGitOriginPath(path string) string { return "" } -// BoolPtr returns pointer to passed boolean +// GetBoolPtr returns pointer to passed boolean func GetBoolPtr(b bool) *bool { return &b }