Skip to content

Commit

Permalink
Add Dev and Project labeling / refactoring labeling and annotations (r…
Browse files Browse the repository at this point in the history
…edhat-developer#5551)

* Add Dev and Project labeling / refactoring labeling and annotations

<!--
Thank you for opening a PR! Here are some things you need to know before submitting:

1. Please read our developer guideline: https://github.com/redhat-developer/odo/wiki/Dev:-odo-Dev-Guidelines
2. Label this PR accordingly with the '/kind' line
3. Ensure you have written and ran the appropriate tests: https://github.com/redhat-developer/odo/wiki/Dev:-Writing-and-running-tests
4. Read how we approve and LGTM each PR: https://github.com/redhat-developer/odo/wiki/Pull-Requests:-Review-guideline

Documentation:

If you are pushing a change to documentation, please read: https://github.com/redhat-developer/odo/wiki/Documentation:-Contributing
-->

**What type of PR is this:**

<!--
Add one of the following kinds:
/kind bug
/kind tests
/kind documentation

Feel free to use other [labels](https://github.com/redhat-developer/odo/labels) as needed. However one of the above labels must be present or the PR will not be reviewed. This instruction is for reviewers as well.
-->
/kind feature
/kind cleanup

**What does this PR do / why we need it:**

In this PR we:
  * Correctly name all the labels in a more concise manner (ex. Project Type
    is an annotation)
  * Add Dev and Deploy mode labels to both `odo dev` and `odo deploy`
    commands so that we can intuitively retrieve what components are in
    deploy or dev mode
  * Somewhat refactors how we do labeling

**Which issue(s) this PR fixes:**
<!--
Specifying the issue will automatically close it when this PR is merged
-->

1/2 of PR's for redhat-developer#5405

**PR acceptance criteria:**

- [X] Unit test

- [X] Integration test

- [X] Documentation

**How to test changes / Special notes to the reviewer:**

N/A

Signed-off-by: Charlie Drage <charlie@charliedrage.com>

* Review update / renaming

Signed-off-by: Charlie Drage <charlie@charliedrage.com>

* Review changes. Adds tests for generateDeploymentObjectMeta. Changes labels

Signed-off-by: Charlie Drage <charlie@charliedrage.com>
  • Loading branch information
cdrage committed Aug 31, 2022
1 parent dcbdb9f commit 593ba0a
Show file tree
Hide file tree
Showing 24 changed files with 220 additions and 179 deletions.
17 changes: 9 additions & 8 deletions pkg/component/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import (
"github.com/devfile/library/pkg/devfile/parser"
parsercommon "github.com/devfile/library/pkg/devfile/parser/data/v2/common"
dfutil "github.com/devfile/library/pkg/util"
"github.com/redhat-developer/odo/pkg/devfile/location"

applabels "github.com/redhat-developer/odo/pkg/application/labels"
componentlabels "github.com/redhat-developer/odo/pkg/component/labels"
"github.com/redhat-developer/odo/pkg/devfile/location"
"github.com/redhat-developer/odo/pkg/envinfo"
"github.com/redhat-developer/odo/pkg/kclient"
"github.com/redhat-developer/odo/pkg/libdevfile"
Expand All @@ -42,7 +42,6 @@ const componentRandomNamePartsMaxLen = 12
const componentNameMaxRetries = 3
const componentNameMaxLen = -1
const NotAvailable = "Not available"

const apiVersion = "odo.dev/v1alpha1"

// GetComponentDir returns source repo name
Expand Down Expand Up @@ -141,7 +140,7 @@ func ListDevfileComponents(client kclient.ClientInterface, selector string) (Com

// create a list of object metadata based on the component and application name (extracted from Deployment labels)
for _, elem := range deploymentList {
component, err := GetComponent(client, elem.Labels[componentlabels.ComponentLabel], elem.Labels[applabels.ApplicationLabel], client.GetCurrentNamespace())
component, err := GetComponent(client, elem.Labels[componentlabels.KubernetesInstanceLabel], elem.Labels[applabels.ApplicationLabel], client.GetCurrentNamespace())
if err != nil {
return ComponentList{}, errors.Wrap(err, "Unable to get component")
}
Expand Down Expand Up @@ -231,6 +230,7 @@ func GetLanguageFromDevfileMetadata(metadata devfile.DevfileMetadata) string {

func getComponentFrom(info localConfigProvider.LocalConfigProvider, componentType string) (Component, error) {
if info.Exists() {

component := newComponentWithType(info.GetName(), componentType)

component.Namespace = info.GetNamespace()
Expand Down Expand Up @@ -328,8 +328,8 @@ func Exists(client kclient.ClientInterface, componentName, applicationName strin
return false, nil
}

func GetComponentState(client kclient.ClientInterface, componentName, applicationName string) State {
// first check if a deployment exists
func GetComponentState(client kclient.ClientInterface, componentName, applicationName string) string {
// Check to see if the deployment has been pushed or not
c, err := GetPushedComponent(client, componentName, applicationName)
if err != nil {
return StateTypeUnknown
Expand Down Expand Up @@ -413,13 +413,14 @@ func getRemoteComponentMetadata(client kclient.ClientInterface, componentName st
// Annotations
component.Annotations = fromCluster.GetAnnotations()

// Mark the component status as pushed
component.Status.State = StateTypePushed

// Labels
component.Labels = fromCluster.GetLabels()

component.Namespace = client.GetCurrentNamespace()
component.Spec.App = applicationName
component.Spec.Env = filteredEnv
component.Status.State = StateTypePushed

return component, nil
}
Expand Down Expand Up @@ -476,7 +477,7 @@ func setLinksServiceNames(client kclient.ClientInterface, linkedSecrets []Secret

serviceCompMap := make(map[string]string)
for _, gotService := range services {
serviceCompMap[gotService.Labels[componentlabels.ComponentLabel]] = gotService.Name
serviceCompMap[gotService.Labels[componentlabels.KubernetesInstanceLabel]] = gotService.Name
}

for _, secret := range secrets {
Expand Down
2 changes: 1 addition & 1 deletion pkg/component/component_full_description.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (cfd *ComponentFullDescription) fillEmptyFields(componentDesc Component, co
// NewComponentFullDescriptionFromClientAndLocalConfigProvider gets the complete description of the component from cluster
func NewComponentFullDescriptionFromClientAndLocalConfigProvider(client kclient.ClientInterface, envInfo *envinfo.EnvSpecificInfo, componentName string, applicationName string, projectName string, context string) (*ComponentFullDescription, error) {
cfd := &ComponentFullDescription{}
var state State
var state string
if client == nil {
state = StateTypeUnknown
} else {
Expand Down
24 changes: 13 additions & 11 deletions pkg/component/component_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

devfilepkg "github.com/devfile/api/v2/pkg/devfile"
"github.com/kylelemons/godebug/pretty"

v1 "k8s.io/api/apps/v1"

Expand Down Expand Up @@ -146,13 +147,13 @@ func TestList(t *testing.T) {
*testingutil.CreateFakeDeployment("comp1"),
}}

deploymentList.Items[0].Labels[componentlabels.ComponentTypeLabel] = "nodejs"
deploymentList.Items[0].Labels[componentlabels.KubernetesNameLabel] = "nodejs"
deploymentList.Items[0].Annotations = map[string]string{
componentlabels.ComponentTypeAnnotation: "nodejs",
componentlabels.OdoProjectTypeAnnotation: "nodejs",
}
deploymentList.Items[1].Labels[componentlabels.ComponentTypeLabel] = "wildfly"
deploymentList.Items[1].Labels[componentlabels.KubernetesNameLabel] = "wildfly"
deploymentList.Items[1].Annotations = map[string]string{
componentlabels.ComponentTypeAnnotation: "wildfly",
componentlabels.OdoProjectTypeAnnotation: "wildfly",
}
tests := []struct {
name string
Expand Down Expand Up @@ -223,6 +224,7 @@ func TestList(t *testing.T) {
}

if !reflect.DeepEqual(tt.output, results) {
t.Errorf("Unexpected output, see the diff in results: %s", pretty.Compare(tt.output, results))
t.Errorf("expected output:\n%#v\n\ngot:\n%#v", tt.output, results)
}
})
Expand Down Expand Up @@ -472,7 +474,7 @@ func TestGetComponentTypeFromDevfileMetadata(t *testing.T) {
}
}

func getFakeComponent(compName, namespace, appName, compType string, state State) Component {
func getFakeComponent(compName, namespace, appName, compType string, state string) Component {
return Component{
TypeMeta: metav1.TypeMeta{
Kind: "Component",
Expand All @@ -482,14 +484,14 @@ func getFakeComponent(compName, namespace, appName, compType string, state State
Name: compName,
Namespace: namespace,
Labels: map[string]string{
applabels.App: appName,
applabels.ManagedBy: "odo",
applabels.ApplicationLabel: appName,
componentlabels.ComponentLabel: compName,
componentlabels.ComponentTypeLabel: compType,
applabels.App: appName,
applabels.ManagedBy: "odo",
applabels.ApplicationLabel: appName,
componentlabels.KubernetesInstanceLabel: compName,
componentlabels.KubernetesNameLabel: compType,
},
Annotations: map[string]string{
componentlabels.ComponentTypeAnnotation: compType,
componentlabels.OdoProjectTypeAnnotation: compType,
},
},
Spec: ComponentSpec{
Expand Down
28 changes: 16 additions & 12 deletions pkg/component/labels/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,34 @@ import (
"github.com/redhat-developer/odo/pkg/util"
)

// ComponentLabel is a label key used to identify the component name
const ComponentLabel = "app.kubernetes.io/instance"
// KubernetesInstanceLabel is a label key used to identify the component name
const KubernetesInstanceLabel = "app.kubernetes.io/instance"

// ComponentTypeLabel is Kubernetes label that identifies the type of a component being used
const ComponentTypeLabel = "app.kubernetes.io/name"
// KubernetesNameLabel is Kubernetes label that identifies the type of a component being used
const KubernetesNameLabel = "app.kubernetes.io/name"

const ComponentTypeAnnotation = "odo.dev/project-type"
// KubernetesManagedByLabel ...
const KubernetesManagedByLabel = "app.kubernetes.io/managed-by"

// ComponentDeployLabel ...
const ComponentDeployLabel = "Deploy"
// ComponentDevName ...
const ComponentDevName = "Dev"

// ComponentModeLabel ...
const ComponentModeLabel = "odo.dev/mode"
// ComponentDeployName ...
const ComponentDeployName = "Deploy"

// ComponentProjectTypeLabel ...
const ComponentProjectTypeLabel = "odo.dev/project-type"
// OdoModeLabel ...
const OdoModeLabel = "odo.dev/mode"

// OdoProjectTypeAnnotation ...
const OdoProjectTypeAnnotation = "odo.dev/project-type"

// GetLabels return labels that should be applied to every object for given component in active application
// additional labels are used only for creating object
// if you are creating something use additional=true
// if you need labels to filter component then use additional=false
func GetLabels(componentName string, applicationName string, additional bool) map[string]string {
labels := applabels.GetLabels(applicationName, additional)
labels[ComponentLabel] = componentName
labels[KubernetesInstanceLabel] = componentName
return labels
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/component/labels/labels_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestGetLabels(t *testing.T) {
},
want: map[string]string{
applabels.ApplicationLabel: "applicationame",
ComponentLabel: "componentname",
KubernetesInstanceLabel: "componentname",
},
}, {
name: "everything with additional",
Expand All @@ -42,7 +42,7 @@ func TestGetLabels(t *testing.T) {
applabels.App: "applicationame",
applabels.ManagedBy: "odo",
applabels.ManagerVersion: version.VERSION,
ComponentLabel: "componentname",
KubernetesInstanceLabel: "componentname",
},
},
}
Expand Down
16 changes: 9 additions & 7 deletions pkg/component/pushed_component.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
v1 "k8s.io/api/apps/v1"
v12 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/klog"
)

type provider interface {
Expand Down Expand Up @@ -143,22 +142,24 @@ func (d devfileComponent) GetEnvVars() []v12.EnvVar {
func (d devfileComponent) GetLabels() map[string]string {
return d.d.Labels
}

func (d devfileComponent) GetAnnotations() map[string]string {
return d.d.Annotations
}

func (d devfileComponent) GetName() string {
return d.d.Labels[componentlabels.ComponentLabel]
return d.d.Labels[componentlabels.KubernetesInstanceLabel]
}

func getType(component provider) (string, error) {
if componentType, ok := component.GetAnnotations()[componentlabels.ComponentTypeAnnotation]; ok {

// For backwards compatibility with previously deployed components that could be non-odo, check the annotation first
// then check to see if there is a label with the project type
if componentType, ok := component.GetAnnotations()[componentlabels.OdoProjectTypeAnnotation]; ok {
return componentType, nil
} else if componentType, ok = component.GetLabels()[componentlabels.OdoProjectTypeAnnotation]; ok {
return componentType, nil
} else if _, ok = component.GetLabels()[componentlabels.ComponentTypeLabel]; ok {
klog.V(1).Info("No annotation assigned; retuning 'Not available' since labels are assigned. Annotations will be assigned when user pushes again.")
return NotAvailable, nil
}

return "", fmt.Errorf("%s component doesn't provide a type annotation; consider pushing the component again", component.GetName())
}

Expand Down Expand Up @@ -202,4 +203,5 @@ func isIgnorableError(err error) bool {
return true
}
return kerrors.IsNotFound(err) || kerrors.IsForbidden(err) || kerrors.IsUnauthorized(err)

}
11 changes: 4 additions & 7 deletions pkg/component/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type SecretMount struct {
// ComponentStatus is Status of components
type ComponentStatus struct {
Context string `json:"context,omitempty"`
State State `json:"state"`
State string `json:"state"`
LinkedServices []SecretMount `json:"linkedServices,omitempty"`
}

Expand All @@ -61,16 +61,13 @@ type CombinedComponentList struct {
OtherComponents []Component `json:"otherComponents"`
}

// State represents the component state
type State string

const (
// StateTypePushed means that Storage is present both locally and on cluster
StateTypePushed State = "Pushed"
StateTypePushed = "Pushed"
// StateTypeNotPushed means that Storage is only in local config, but not on the cluster
StateTypeNotPushed State = "Not Pushed"
StateTypeNotPushed = "Not Pushed"
// StateTypeUnknown means that odo cannot tell its state
StateTypeUnknown State = "Unknown"
StateTypeUnknown = "Unknown"
)

func newComponentWithType(componentName, componentType string) Component {
Expand Down
16 changes: 10 additions & 6 deletions pkg/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,26 @@ func (o *deployHandler) ApplyImage(img v1alpha2.Component) error {
return image.BuildPushSpecificImage(o.devfileObj, o.path, img, true)
}

// ApplyKubernetes applies inline Kubernetes YAML from the devfile.yaml file
func (o *deployHandler) ApplyKubernetes(kubernetes v1alpha2.Component) error {
// validate if the GVRs represented by Kubernetes inlined components are supported by the underlying cluster

// Validate if the GVRs represented by Kubernetes inlined components are supported by the underlying cluster
_, err := service.ValidateResourceExist(o.kubeClient, kubernetes, o.path)
if err != nil {
return err
}

// Get the most common labels that's applicable to all resources being deployed.
// Retrieve the component type from the devfile and also inject it into the list of labels
// Set the mode to DEPLOY. Regardless of what Kubernetes resource we are deploying.
labels := componentlabels.GetLabels(o.devfileObj.Data.GetMetadata().Name, o.appName, true)
componentType := component.GetComponentTypeFromDevfileMetadata(o.devfileObj.Data.GetMetadata())
labels[componentlabels.ComponentProjectTypeLabel] = componentType
labels[componentlabels.ComponentModeLabel] = componentlabels.ComponentDeployLabel
labels[componentlabels.OdoModeLabel] = componentlabels.ComponentDeployName
klog.V(4).Infof("Injecting labels: %+v into k8s artifact", labels)

// Create the annotations
// Retrieve the component type from the devfile and also inject it into the list of annotations
annotations := make(map[string]string)
annotations[componentlabels.OdoProjectTypeAnnotation] = component.GetComponentTypeFromDevfileMetadata(o.devfileObj.Data.GetMetadata())

// Get the Kubernetes component
u, err := service.GetK8sComponentAsUnstructured(kubernetes.Kubernetes, o.path, devfilefs.DefaultFs{})
if err != nil {
Expand All @@ -79,7 +83,7 @@ func (o *deployHandler) ApplyKubernetes(kubernetes v1alpha2.Component) error {

// Deploy the actual Kubernetes component and error out if there's an issue.
log.Infof("\nDeploying Kubernetes %s: %s", u.GetKind(), u.GetName())
isOperatorBackedService, err := service.PushKubernetesResource(o.kubeClient, u, labels)
isOperatorBackedService, err := service.PushKubernetesResource(o.kubeClient, u, labels, annotations)
if err != nil {
return errors.Wrap(err, "failed to create service(s) associated with the component")
}
Expand Down
Loading

0 comments on commit 593ba0a

Please sign in to comment.