Add runtime label based on metadata:language/projectType (#6112)

* Add runtime label based on metadata:language/projectType

* Add integration tests for odo dev

* odo deploy integration test

* Reverse priority between pojectType and language
This commit is contained in:
Philippe Martin
2022-09-14 09:22:45 +02:00
committed by GitHub
parent 16c6d1589f
commit 32303b164d
19 changed files with 495 additions and 14 deletions

View File

@@ -42,6 +42,15 @@ func GetComponentTypeFromDevfileMetadata(metadata devfile.DevfileMetadata) strin
return componentType
}
// GetComponentRuntimeFromDevfileMetadata returns the Project Type defined in the Devfile metadata
// or if not set, the Language
func GetComponentRuntimeFromDevfileMetadata(metadata devfile.DevfileMetadata) string {
if metadata.ProjectType != "" {
return metadata.ProjectType
}
return metadata.Language
}
// GatherName computes and returns what should be used as name for the Devfile object specified.
//
// If a non-blank name is available in the Devfile metadata (which is optional), it is sanitized and returned.

View File

@@ -291,11 +291,11 @@ func TestDeleteComponentClient_ListResourcesToDeleteFromDevfile(t *testing.T) {
// labeledOuterloopResource is the deployment with labels set
labeledOuterloopResource := *outerLoopResourceUnstructured.DeepCopy()
labeledOuterloopResource.SetLabels(odolabels.GetLabels(compName, appName, odolabels.ComponentDeployMode, false))
labeledOuterloopResource.SetLabels(odolabels.GetLabels(compName, appName, "", odolabels.ComponentDeployMode, false))
// innerLoopResourceUnstructured is the deployment that will be deployed by apply command with `odo dev`
innerLoopResourceUnstructured := *outerLoopResourceUnstructured.DeepCopy()
innerLoopResourceUnstructured.SetLabels(odolabels.GetLabels(compName, appName, odolabels.ComponentDevMode, false))
innerLoopResourceUnstructured.SetLabels(odolabels.GetLabels(compName, appName, "", odolabels.ComponentDevMode, false))
deploymentRESTMapping := meta.RESTMapping{
Resource: getGVR("apps", "v1", "Deployment"),

View File

@@ -114,7 +114,8 @@ func (a Adapter) Push(parameters adapters.PushParameters, componentStatus *watch
}
// Set the mode to Dev since we are using "odo dev" here
labels := odolabels.GetLabels(a.ComponentName, a.AppName, odolabels.ComponentDevMode, false)
runtime := component.GetComponentRuntimeFromDevfileMetadata(a.Devfile.Data.GetMetadata())
labels := odolabels.GetLabels(a.ComponentName, a.AppName, runtime, odolabels.ComponentDevMode, false)
var updated bool
deployment, updated, err = a.createOrUpdateComponent(deploymentExists, parameters.EnvSpecificInfo, libdevfile.DevfileCommands{
@@ -327,9 +328,12 @@ func (a *Adapter) createOrUpdateComponent(
ei.SetDevfileObj(a.Devfile)
componentName := a.ComponentName
runtime := component.GetComponentRuntimeFromDevfileMetadata(a.Devfile.Data.GetMetadata())
storageClient := storagepkg.NewClient(componentName, a.AppName, storagepkg.ClientOptions{
Client: a.kubeClient,
LocalConfigProvider: &ei,
Runtime: runtime,
})
// handle the ephemeral storage
@@ -345,7 +349,7 @@ func (a *Adapter) createOrUpdateComponent(
}
// Set the labels
labels := odolabels.GetLabels(componentName, a.AppName, odolabels.ComponentDevMode, true)
labels := odolabels.GetLabels(componentName, a.AppName, runtime, odolabels.ComponentDevMode, true)
annotations := make(map[string]string)
odolabels.SetProjectType(annotations, component.GetComponentTypeFromDevfileMetadata(a.AdapterContext.Devfile.Data.GetMetadata()))

View File

@@ -146,7 +146,8 @@ func ApplyKubernetes(mode, appName string, componentName string, devfile parser.
// Get the most common labels that's applicable to all resources being deployed.
// Set the mode. Regardless of what Kubernetes resource we are deploying.
labels := odolabels.GetLabels(componentName, appName, mode, false)
runtime := component.GetComponentRuntimeFromDevfileMetadata(devfile.Data.GetMetadata())
labels := odolabels.GetLabels(componentName, appName, runtime, mode, false)
klog.V(4).Infof("Injecting labels: %+v into k8s artifact", labels)

View File

@@ -20,6 +20,8 @@ const (
// kubernetesStorageNameLabel is applied to all storage resources that are created
kubernetesStorageNameLabel = "app.kubernetes.io/storage-name"
openshiftRunTimeLabel = "app.openshift.io/runtime"
// odoModeLabel indicates which command were used to create the component, either dev or deploy
odoModeLabel = "odo.dev/mode"

View File

@@ -12,8 +12,11 @@ import (
// if you need labels to filter component then use GetSelector instead
// Note: isPartOfComponent denotes if the label is required for a core resource(deployment, svc, pvc, pv) of a given component deployed with `odo dev`;
// it is the only thing that sets it apart from the resources created via other ways (`odo deploy`, deploying resource with apply command during `odo dev`)
func GetLabels(componentName string, applicationName string, mode string, isPartOfComponent bool) map[string]string {
func GetLabels(componentName string, applicationName string, runtime string, mode string, isPartOfComponent bool) map[string]string {
labels := getLabels(componentName, applicationName, mode, true, isPartOfComponent)
if runtime != "" {
labels[openshiftRunTimeLabel] = runtime
}
return labels
}

View File

@@ -12,7 +12,6 @@ import (
"k8s.io/klog"
"github.com/redhat-developer/odo/pkg/kclient"
"github.com/redhat-developer/odo/pkg/labels"
odolabels "github.com/redhat-developer/odo/pkg/labels"
)
@@ -41,7 +40,7 @@ func (k kubernetesClient) Create(storage Storage) error {
return err
}
labels := odolabels.GetLabels(k.componentName, k.appName, odolabels.ComponentDevMode, false)
labels := odolabels.GetLabels(k.componentName, k.appName, k.runtime, odolabels.ComponentDevMode, false)
odolabels.AddStorageInfo(labels, storage.Name, strings.Contains(storage.Name, OdoSourceVolume))
objectMeta := generator.GetObjectMeta(pvcName, k.client.GetCurrentNamespace(), labels, nil)
@@ -138,7 +137,7 @@ func (k kubernetesClient) List() (StorageList, error) {
return StorageList{}, nil
}
selector := labels.SelectorBuilder().WithComponent(k.componentName).WithoutSourcePVC(OdoSourceVolume).Selector()
selector := odolabels.SelectorBuilder().WithComponent(k.componentName).WithoutSourcePVC(OdoSourceVolume).Selector()
pvcs, err := k.client.ListPVCs(selector)
if err != nil {
return StorageList{}, fmt.Errorf("unable to get PVC using selector %q: %w", selector, err)

View File

@@ -428,7 +428,7 @@ func Test_kubernetesClient_Create(t *testing.T) {
t.Errorf("failed to create quantity by calling resource.ParseQuantity(%v)", tt.args.storage.Spec.Size)
}
wantLabels := odolabels.GetLabels(tt.fields.generic.componentName, tt.fields.generic.appName, odolabels.ComponentDevMode, false)
wantLabels := odolabels.GetLabels(tt.fields.generic.componentName, tt.fields.generic.appName, "", odolabels.ComponentDevMode, false)
odolabels.AddStorageInfo(wantLabels, tt.args.storage.Name, strings.Contains(tt.args.storage.Name, OdoSourceVolume))
// created PVC should be labeled with labels passed to CreatePVC

View File

@@ -29,12 +29,14 @@ type generic struct {
appName string
componentName string
localConfigProvider localConfigProvider.LocalConfigProvider
runtime string
}
type ClientOptions struct {
Client kclient.ClientInterface
LocalConfigProvider localConfigProvider.LocalConfigProvider
Deployment *v1.Deployment
Runtime string
}
type Client interface {
@@ -55,6 +57,7 @@ func NewClient(componentName string, appName string, options ClientOptions) Clie
genericInfo.componentName = componentName
genericInfo.appName = appName
genericInfo.runtime = options.Runtime
return kubernetesClient{
generic: genericInfo,

View File

@@ -12,7 +12,7 @@ import (
)
func getStorageLabels(storageName, componentName, applicationName string) map[string]string {
labels := odolabels.GetLabels(componentName, applicationName, odolabels.ComponentDevMode, false)
labels := odolabels.GetLabels(componentName, applicationName, "", odolabels.ComponentDevMode, false)
odolabels.AddStorageInfo(labels, storageName, false)
return labels
}

View File

@@ -12,7 +12,7 @@ func CreateFakePod(componentName, podName string) *corev1.Pod {
fakePod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
Labels: odolabels.GetLabels(componentName, "app", odolabels.ComponentDevMode, false),
Labels: odolabels.GetLabels(componentName, "app", "", odolabels.ComponentDevMode, false),
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,

View File

@@ -8,7 +8,7 @@ import (
)
func FakeKubeService(componentName, serviceName string) corev1.Service {
labels := odolabels.GetLabels(componentName, "app", odolabels.ComponentDevMode, false)
labels := odolabels.GetLabels(componentName, "app", "", odolabels.ComponentDevMode, false)
return corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: serviceName,

View File

@@ -0,0 +1,93 @@
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
- id: deploy
composite:
commands:
- deploy-nginx
group:
kind: deploy
isDefault: true
- id: deploy-nginx
apply:
component: nginx
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
- name: nginx
kubernetes:
inlined: |
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
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-prj
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

View File

@@ -0,0 +1,92 @@
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
- id: deploy
composite:
commands:
- deploy-nginx
group:
kind: deploy
isDefault: true
- id: deploy-nginx
apply:
component: nginx
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
- name: nginx
kubernetes:
inlined: |
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
metadata:
description: Stack with Node.js 14
displayName: Node.js Runtime
icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg
name: nodejs-prj
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

View File

@@ -0,0 +1,94 @@
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
- id: deploy
composite:
commands:
- deploy-nginx
group:
kind: deploy
isDefault: true
- id: deploy-nginx
apply:
component: nginx
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
- name: nginx
kubernetes:
inlined: |
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
metadata:
description: Stack with Node.js 14
displayName: Node.js Runtime
icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg
name: nodejs-prj
projectType: nodejs
language: javascript
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

View File

@@ -51,4 +51,6 @@ type CliRunner interface {
GetBindableKinds() (string, string)
GetServiceBinding(name, projectName string) (string, string)
GetLogs(podName string) string
AssertContainsLabel(kind, namespace, componentName, appName, mode, key, value string)
AssertNoContainsLabel(kind, namespace, componentName, appName, mode, key string)
}

View File

@@ -407,3 +407,15 @@ func (kubectl KubectlRunner) GetLogs(podName string) string {
output := Cmd(kubectl.path, "logs", podName).ShouldPass().Out()
return output
}
func (kubectl KubectlRunner) AssertContainsLabel(kind, namespace, componentName, appName, mode, key, value string) {
selector := labels.Builder().WithComponentName(componentName).WithAppName(appName).WithMode(mode).SelectorFlag()
all := Cmd(kubectl.path, "get", kind, selector, "-n", namespace, "-o", "jsonpath={.items[0].metadata.labels}").ShouldPass().Out()
Expect(all).To(ContainSubstring(fmt.Sprintf(`"%s":"%s"`, key, value)))
}
func (kubectl KubectlRunner) AssertNoContainsLabel(kind, namespace, componentName, appName, mode, key string) {
selector := labels.Builder().WithComponentName(componentName).WithAppName(appName).WithMode(mode).SelectorFlag()
all := Cmd(kubectl.path, "get", kind, selector, "-n", namespace, "-o", "jsonpath={.items[0].metadata.labels}").ShouldPass().Out()
Expect(all).ToNot(ContainSubstring(fmt.Sprintf(`"%s"`, key)))
}

View File

@@ -528,7 +528,7 @@ func (oc OcRunner) doAsAdmin(clusterType string) string {
return token
}
// doAsDeveloper logins as developer to perform some task
// doAsDeveloper logins as developer to perform some task
func (oc OcRunner) doAsDeveloper(token, clusterType string) {
if clusterType == "IBM" {
@@ -599,3 +599,15 @@ func (oc OcRunner) GetLogs(podName string) string {
output := Cmd(oc.path, "logs", podName).ShouldPass().Out()
return output
}
func (oc OcRunner) AssertContainsLabel(kind, namespace, componentName, appName, mode, key, value string) {
selector := labels.Builder().WithComponentName(componentName).WithAppName(appName).WithMode(mode).SelectorFlag()
all := Cmd(oc.path, "get", kind, selector, "-n", namespace, "-o", "jsonpath={.items[0].metadata.labels}").ShouldPass().Out()
Expect(all).To(ContainSubstring(fmt.Sprintf(`"%s":"%s"`, key, value)))
}
func (oc OcRunner) AssertNoContainsLabel(kind, namespace, componentName, appName, mode, key string) {
selector := labels.Builder().WithComponentName(componentName).WithAppName(appName).WithMode(mode).SelectorFlag()
all := Cmd(oc.path, "get", kind, selector, "-n", namespace, "-o", "jsonpath={.items[0].metadata.labels}").ShouldPass().Out()
Expect(all).ToNot(ContainSubstring(fmt.Sprintf(`"%s"`, key)))
}

View File

@@ -17,6 +17,7 @@ import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/redhat-developer/odo/pkg/labels"
"github.com/redhat-developer/odo/pkg/remotecmd"
segment "github.com/redhat-developer/odo/pkg/segment/context"
"github.com/redhat-developer/odo/pkg/storage"
@@ -2515,4 +2516,158 @@ CMD ["npm", "start"]
})
})
})
for _, t := range []struct {
whenTitle string
devfile string
checkDev func(cmpName string)
checkDeploy func(cmpName string)
}{
{
whenTitle: "Devfile contains metadata.language",
devfile: "devfile-with-metadata-language.yaml",
checkDev: func(cmpName string) {
commonVar.CliRunner.AssertContainsLabel(
"deployment",
commonVar.Project,
cmpName,
"app",
labels.ComponentDevMode,
"app.openshift.io/runtime",
"javascript",
)
commonVar.CliRunner.AssertContainsLabel(
"service",
commonVar.Project,
cmpName,
"app",
labels.ComponentDevMode,
"app.openshift.io/runtime",
"javascript",
)
},
checkDeploy: func(cmpName string) {
commonVar.CliRunner.AssertContainsLabel(
"deployment",
commonVar.Project,
cmpName,
"app",
labels.ComponentDeployMode,
"app.openshift.io/runtime",
"javascript",
)
},
},
{
whenTitle: "Devfile contains metadata.projectType",
devfile: "devfile-with-metadata-project-type.yaml",
checkDev: func(cmpName string) {
commonVar.CliRunner.AssertContainsLabel(
"deployment",
commonVar.Project,
cmpName,
"app",
labels.ComponentDevMode,
"app.openshift.io/runtime",
"nodejs",
)
commonVar.CliRunner.AssertContainsLabel(
"service",
commonVar.Project,
cmpName,
"app",
labels.ComponentDevMode,
"app.openshift.io/runtime",
"nodejs",
)
},
checkDeploy: func(cmpName string) {
commonVar.CliRunner.AssertContainsLabel(
"deployment",
commonVar.Project,
cmpName,
"app",
labels.ComponentDeployMode,
"app.openshift.io/runtime",
"nodejs",
)
},
},
{
whenTitle: "Devfile contains neither metadata.language nor metadata.projectType",
devfile: "devfile-with-metadata-no-language-project-type.yaml",
checkDev: func(cmpName string) {
commonVar.CliRunner.AssertNoContainsLabel(
"deployment",
commonVar.Project,
cmpName,
"app",
labels.ComponentDevMode,
"app.openshift.io/runtime",
)
commonVar.CliRunner.AssertNoContainsLabel(
"service",
commonVar.Project,
cmpName,
"app",
labels.ComponentDevMode,
"app.openshift.io/runtime",
)
},
checkDeploy: func(cmpName string) {
commonVar.CliRunner.AssertNoContainsLabel(
"deployment",
commonVar.Project,
cmpName,
"app",
labels.ComponentDeployMode,
"app.openshift.io/runtime",
)
},
},
} {
t := t
When(t.whenTitle, func() {
var cmpName = "nodejs-prj" // from devfile
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", t.devfile),
filepath.Join(commonVar.Context, "devfile.yaml"))
})
When("running odo dev", func() {
var devSession helper.DevSession
BeforeEach(func() {
var err error
devSession, _, _, _, err = helper.StartDevMode(nil)
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
devSession.Stop()
})
It("should set the correct value in labels of resources", func() {
t.checkDev(cmpName)
})
})
When("odo deploy is executed", func() {
BeforeEach(func() {
helper.Cmd("odo", "deploy").ShouldPass()
})
AfterEach(func() {
helper.Cmd("odo", "delete", "component", "--force")
})
It("should set the correct value in labels of deployed resources", func() {
t.checkDeploy(cmpName)
})
})
})
}
})