Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- `odo push` undeploys Operator backed services marked as managed by the current devfile not present in this devfile anymore ([#4761](https://github.com/openshift/odo/pull/4761))
- param based `odo service create` for operator backed services ([#4704](https://github.com/openshift/odo/pull/4704))
- add `odo catalog describe service <operator> --example` ([#4821](https://github.com/openshift/odo/pull/4821))
- `odo link` and `odo unlink` write to devfile without deploying to cluster. Deploying happens when running `odo push` ([#4819](https://github.com/openshift/odo/pull/4819))

### Bug Fixes

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ openshiftci-presubmit-unittests:

.PHONY: test-operator-hub
test-operator-hub: ## Run OperatorHub tests
$(RUN_GINKGO) $(GINKGO_FLAGS) -focus="odo service command tests" tests/integration/operatorhub/
$(RUN_GINKGO) $(GINKGO_FLAGS) tests/integration/operatorhub/

.PHONY: test-cmd-devfile-describe
test-cmd-devfile-describe:
Expand Down
50 changes: 32 additions & 18 deletions pkg/devfile/adapters/kubernetes/component/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ import (
"strings"
"time"

"github.com/openshift/odo/pkg/service"

"github.com/devfile/library/pkg/devfile/generator"
componentlabels "github.com/openshift/odo/pkg/component/labels"
"github.com/openshift/odo/pkg/envinfo"
"github.com/openshift/odo/pkg/service"
"github.com/openshift/odo/pkg/util"

appsv1 "k8s.io/api/apps/v1"
Expand Down Expand Up @@ -111,6 +110,7 @@ type Adapter struct {
// Push updates the component if a matching component exists or creates one if it doesn't exist
// Once the component has started, it will sync the source code to it.
func (a Adapter) Push(parameters common.PushParameters) (err error) {

a.deployment, err = a.Client.GetKubeClient().GetOneDeployment(a.ComponentName, a.AppName)
if err != nil {
if _, ok := err.(*kclient.DeploymentNotFoundError); !ok {
Expand Down Expand Up @@ -160,6 +160,29 @@ func (a Adapter) Push(parameters common.PushParameters) (err error) {
}
s.End(true)

log.Info("\nUpdating services")
Comment thread
mik-dass marked this conversation as resolved.
// fetch the "kubernetes inlined components" to create them on cluster
// from odo standpoint, these components contain yaml manifest of an odo service or an odo link
k8sComponents, err := a.Devfile.Data.GetComponents(parsercommon.DevfileOptions{
ComponentOptions: parsercommon.ComponentOptions{ComponentType: devfilev1.KubernetesComponentType},
})
if err != nil {
return errors.Wrap(err, "error while trying to fetch service(s) from devfile")
}
labels := componentlabels.GetLabels(a.ComponentName, a.AppName, true)
// create the Kubernetes objects from the manifest and delete the ones not in the devfile
needRestart, err := service.PushServiceFromKubernetesInlineComponents(a.Client.GetKubeClient(), k8sComponents, labels)
if err != nil {
return errors.Wrap(err, "failed to create service(s) associated with the component")
}

if componentExists && needRestart {
err = a.Client.GetKubeClient().WaitForPodNotReady(podName)
if err != nil {
return err
}
}

log.Infof("\nCreating Kubernetes resources for component %s", a.ComponentName)

previousMode := parameters.EnvSpecificInfo.GetRunMode()
Expand All @@ -183,21 +206,6 @@ func (a Adapter) Push(parameters common.PushParameters) (err error) {
return errors.Wrap(err, "unable to create or update component")
}

// fetch the "kubernetes inlined components" to create them on cluster
// from odo standpoint, these components contain yaml manifest of an odo service or an odo link
k8sComponents, err := a.Devfile.Data.GetComponents(parsercommon.DevfileOptions{
ComponentOptions: parsercommon.ComponentOptions{ComponentType: devfilev1.KubernetesComponentType},
})
if err != nil {
return errors.Wrap(err, "error while trying to fetch service(s) from devfile")
}
labels := componentlabels.GetLabels(a.ComponentName, a.AppName, true)
// create the Kubernetes objects from the manifest and delete the ones not in the devfile
err = service.PushServiceFromKubernetesInlineComponents(a.Client.GetKubeClient(), k8sComponents, labels)
if err != nil {
return errors.Wrap(err, "failed to create service(s) associated with the component")
}

a.deployment, err = a.Client.GetKubeClient().WaitForDeploymentRollout(a.deployment.Name)
if err != nil {
return errors.Wrap(err, "error while waiting for deployment rollout")
Expand All @@ -215,17 +223,23 @@ func (a Adapter) Push(parameters common.PushParameters) (err error) {
return err
}

ownerReference := generator.GetOwnerReference(a.deployment)
// update the owner reference of the PVCs with the deployment
for i := range pvcs {
if pvcs[i].OwnerReferences != nil || pvcs[i].DeletionTimestamp != nil {
continue
}
err = a.Client.GetKubeClient().UpdateStorageOwnerReference(&pvcs[i], generator.GetOwnerReference(a.deployment))
err = a.Client.GetKubeClient().UpdateStorageOwnerReference(&pvcs[i], ownerReference)
if err != nil {
return err
}
}

err = service.UpdateKubernetesInlineComponentsOwnerReferences(a.Client.GetKubeClient(), k8sComponents, ownerReference)
if err != nil {
return err
}

parameters.EnvSpecificInfo.SetDevfileObj(a.Devfile)
err = component.ApplyConfig(&a.Client, config.LocalConfigInfo{}, parameters.EnvSpecificInfo, color.Output, componentExists, false)
if err != nil {
Expand Down
65 changes: 1 addition & 64 deletions pkg/envinfo/envinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ type ComponentSettings struct {
URL *[]localConfigProvider.LocalURL `yaml:"Url,omitempty" json:"url,omitempty"`
// AppName is the application name. Application is a virtual concept present in odo used
// for grouping of components. A namespace can contain multiple applications
AppName string `yaml:"AppName,omitempty" json:"appName,omitempty"`
Link *[]EnvInfoLink `yaml:"Link,omitempty" json:"link,omitempty"`
AppName string `yaml:"AppName,omitempty" json:"appName,omitempty"`

// DebugPort controls the port used by the pod to run the debugging agent on
DebugPort *int `yaml:"DebugPort,omitempty" json:"debugPort,omitempty"`
Expand Down Expand Up @@ -88,15 +87,6 @@ type EnvSpecificInfo struct {
envinfoFileExists bool
}

type EnvInfoLink struct {
// Name of link (same as name of k8s secret)
Name string `yaml:"Name,omitempty" json:"name,omitempty"`
// Kind of service with which the component is linked
ServiceKind string `yaml:"ServiceKind,omitempty" json:"serviceKind,omitempty"`
// Name of the instance of the ServiceKind that component is linked with
ServiceName string `yaml:"ServiceName,omitempty" json:"serviceName,omitempty"`
}

func WrapForJSONOutput(compSettings ComponentSettings) JSONEnvInfoRepr {
return JSONEnvInfoRepr{
TypeMeta: metav1.TypeMeta{
Expand Down Expand Up @@ -216,14 +206,6 @@ func (esi *EnvSpecificInfo) SetConfiguration(parameter string, value interface{}
} else {
esi.componentSettings.URL = &[]localConfigProvider.LocalURL{urlValue}
}

case "link":
linkValue := value.(EnvInfoLink)
if esi.componentSettings.Link != nil {
*esi.componentSettings.Link = append(*esi.componentSettings.Link, linkValue)
} else {
esi.componentSettings.Link = &[]EnvInfoLink{linkValue}
}
}

return esi.writeToFile()
Expand Down Expand Up @@ -305,26 +287,6 @@ func (esi *EnvSpecificInfo) DeleteConfiguration(parameter string) error {

}

func (esi *EnvSpecificInfo) DeleteLink(parameter string) error {
index := -1

for i, link := range *esi.componentSettings.Link {
if link.Name == parameter {
index = i
break
}
}

if index != -1 {
s := *esi.componentSettings.Link
s = append(s[:index], s[index+1:]...)
esi.componentSettings.Link = &s
return esi.writeToFile()
} else {
return nil
}
}

// GetComponentSettings returns the componentSettings from envinfo
func (esi *EnvSpecificInfo) GetComponentSettings() ComponentSettings {
return esi.componentSettings
Expand Down Expand Up @@ -431,26 +393,6 @@ func (ei *EnvInfo) SetIsRouteSupported(isRouteSupported bool) {
ei.isRouteSupported = isRouteSupported
}

// GetLink returns the EnvInfoLink, returns default if nil
func (ei *EnvInfo) GetLink() []EnvInfoLink {
if ei.componentSettings.Link == nil {
return []EnvInfoLink{}
}
return *ei.componentSettings.Link
}

// SearchLinkName searches for a Link with given service kind and service name
// and returns its name if found
func (ei *EnvInfo) SearchLinkName(serviceKind, serviceName string) (string, bool) {
links := ei.GetLink()
for _, link := range links {
if link.ServiceKind == serviceKind && link.ServiceName == serviceName {
return link.Name, true
}
}
return "", false
}

const (
// Name is the name of the setting controlling the component name
Name = "Name"
Expand All @@ -472,10 +414,6 @@ const (
Push = "PUSH"
// PushDescription is the description of push parameter
PushDescription = "Push parameter is the action to write devfile commands to env.yaml"
// Link parameter
Link = "LINK"
// LinkDescription is the description of Link
LinkDescription = "Link to an Operator backed service"
)

var (
Expand All @@ -485,7 +423,6 @@ var (
DebugPort: DebugPortDescription,
URL: URLDescription,
Push: PushDescription,
Link: LinkDescription,
}

lowerCaseLocalParameters = util.GetLowerCaseParameters(GetLocallySupportedParameters())
Expand Down
69 changes: 0 additions & 69 deletions pkg/envinfo/envinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -893,75 +893,6 @@ func TestRemoveEndpointInDevfile(t *testing.T) {
}
}

func TestSearchLinkName(t *testing.T) {
tests := []struct {
name string
ei *EnvInfo
serviceKind string
serviceName string
want string
wantFound bool
}{
{
name: "Case 1: Existing link",
ei: &EnvInfo{
componentSettings: ComponentSettings{
Link: &[]EnvInfoLink{
{
Name: "a first name",
ServiceKind: "a first kind",
ServiceName: "a first service name",
},
{
Name: "a name",
ServiceKind: "a kind",
ServiceName: "a service name",
},
},
},
},
serviceKind: "a kind",
serviceName: "a service name",
want: "a name",
wantFound: true,
},
{
name: "Case 2: Non existing link",
ei: &EnvInfo{
componentSettings: ComponentSettings{
Link: &[]EnvInfoLink{
{
Name: "a first name",
ServiceKind: "a first kind",
ServiceName: "a first service name",
},
{
Name: "a name",
ServiceKind: "a kind",
ServiceName: "a service name",
},
},
},
},
serviceKind: "an unknown kind",
serviceName: "a service name",
wantFound: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, found := tt.ei.SearchLinkName(tt.serviceKind, tt.serviceName)
if found != tt.wantFound {
t.Errorf("Expected found %v, got %v", tt.wantFound, found)
}
if found && result != tt.want {
t.Errorf("Expected %q, got %q", tt.want, result)
}
})
}
}

func createDirectoryAndFile(create bool, fs filesystem.Filesystem, odoDir string) error {
if !create {
return nil
Expand Down
Loading