Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create services together with deployment during devfile push #2679

Merged

Conversation

yangcao77
Copy link
Contributor

@yangcao77 yangcao77 commented Mar 4, 2020

What type of PR is this?
/kind feature

What does does this PR do / why we need it:
This PR creates service during devfile push using the port name and number provided within the devfile. Also updates the service if the deployment get updated.

$ kubectl describe service springtest1
Name:              springtest1
Namespace:         che
Labels:            component=springtest1
Annotations:       <none>
Selector:          component=springtest1
Type:              ClusterIP
IP:                172.30.100.64
Port:              9090-tcp  9090/TCP
TargetPort:        9090/TCP
Endpoints:         10.129.0.231:9090
Port:              8080-tcp  8080/TCP
TargetPort:        8080/TCP
Endpoints:         10.129.0.231:8080
Session Affinity:  None
Events:            <none>

Which issue(s) this PR fixes:
#2622

How to test changes / Special notes to the reviewer:

To test create
git clone Clone https://github.com/rajivnathan/openLibertyDevfile

odo preference set experimental true
 ./odo push --devfile ./devfile.yaml

Verify a service with the component name has been created.
Describe the service, verify the port matches the endpoint in the devfile.

To test update:
modify the port number to 8080 in devfile

 ./odo push --devfile ./devfile.yaml

Describe the service, verify the port has been changed to 8080

Signed-off-by: Stephanie <stephanie.cao@ibm.com>
Signed-off-by: Stephanie <stephanie.cao@ibm.com>
Signed-off-by: Stephanie <stephanie.cao@ibm.com>
Signed-off-by: Stephanie <stephanie.cao@ibm.com>
@openshift-ci-robot openshift-ci-robot added the kind/feature Categorizes issue as a feature request. For PRs, that means that the PR is the implementation label Mar 4, 2020
@openshift-ci-robot
Copy link
Collaborator

Hi @yangcao77. Thanks for your PR.

I'm waiting for a openshift member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@openshift-ci-robot openshift-ci-robot added needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. Required by Prow. size/L labels Mar 4, 2020
@codecov
Copy link

codecov bot commented Mar 4, 2020

Codecov Report

Merging #2679 into master will increase coverage by 0.35%.
The diff coverage is 69.84%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2679      +/-   ##
==========================================
+ Coverage   43.39%   43.74%   +0.35%     
==========================================
  Files          86       87       +1     
  Lines        7907     7967      +60     
==========================================
+ Hits         3431     3485      +54     
- Misses       4136     4143       +7     
+ Partials      340      339       -1
Impacted Files Coverage Δ
pkg/kclient/services.go 100% <100%> (ø)
pkg/kclient/generators.go 92.63% <100%> (+1.38%) ⬆️
...g/devfile/adapters/kubernetes/component/adapter.go 34.19% <38.7%> (+0.25%) ⬆️
pkg/odo/cli/service/create.go 20% <0%> (ø) ⬆️
pkg/devfile/parser/apiVersion.go 76.47% <0%> (ø) ⬆️
pkg/devfile/parser/content.go 83.33% <0%> (+0.72%) ⬆️
pkg/component/watch.go 74% <0%> (+6.66%) ⬆️
pkg/devfile/parser/fs.go 100% <0%> (+100%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 57756ed...e80367d. Read the comment docs.

Signed-off-by: Stephanie <stephanie.cao@ibm.com>
@mik-dass
Copy link
Contributor

mik-dass commented Mar 5, 2020

/ok-to-test

@openshift-ci-robot openshift-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. Required by Prow. labels Mar 5, 2020
Copy link
Contributor

@amitkrout amitkrout left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yangcao77 Can you please add integration test for the changes. The test will be mostly user facing scenario like what you describe in How to test changes of your pr description.

How to write:

Create a separate test file, for example devfile_test.go in tests/integration/

devfile_test.go

package integration

import (
	"os"
	"path/filepath"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/openshift/odo/tests/helper"
)

var _ = Describe("odo storage command tests", func() {
	var project string
	var context string

	// This is run after every Spec (It)
	var _ = BeforeEach(func() {
		SetDefaultEventuallyTimeout(10 * time.Minute)
		SetDefaultConsistentlyDuration(30 * time.Second)
		context = helper.CreateNewContext()
		os.Setenv("GLOBALODOCONFIG", filepath.Join(context, "config.yaml"))
		project = helper.CreateRandProject()
		oc = helper.NewOcRunner("oc")
	})

	// Clean up after the test
	// This is run after every Spec (It)
	var _ = AfterEach(func() {
		helper.DeleteProject(project)
		helper.DeleteDir(context)
		os.Unsetenv("GLOBALODOCONFIG")
	})

	Context("when running devfile push", func() {
		It("should create services together with deployment", func() {
			helper.CmdShouldPass("git", "clone", "https://github.com/rajivnathan/openLibertyDevfile", filepath.Join(context,"openLibertyDevfile"))
			helper.CmdShouldPass("odo", "preference", "set", "experimental", "true")
			helper.CmdShouldPass("odo", "push", "--devfile", filepath.Join(context,"openLibertyDevfile","devfile.yaml"))
			
			// modify the port number to 8080 in devfile. You need to write a 
			// new helper function in tests/integration/helper/helper_filesystem.go
			// and call it here for example helper.yamlupdate()

			helper.CmdShouldPass("odo", "push", "--devfile", filepath.Join(context,"openLibertyDevfile","devfile.yaml"))

			// verify Describe the service using oc command, verify the port has been changed to 8080

		})
	})
})

Basically we use ginkgo framework for integration test. You can visit ginkgo documentation for more details - https://onsi.github.io/ginkgo/ plus go through odo integration tests here in tests/integration/ for how we have been doing it. I am sure you will get more tips for writing the integration test.

@yangcao77
Copy link
Contributor Author

yangcao77 commented Mar 5, 2020

@amitkrout We do not have any integration tests for devfile support yet. The integration tests will be added later on. We have an issue opened to track that: #2673
(Currently the integration tests issue is blocked by this issue: #2684 - no namespace saved in env.yaml file)

@amitkrout
Copy link
Contributor

@amitkrout We do not have any integration tests for devfile support yet. The integration tests will be added later on. We have an issue opened to track that: #2673
(Currently the integration tests issue is blocked by this issue: #2684 - no namespace saved in env.yaml file)

Understood. Thanks


service, err := c.KubeClient.CoreV1().Services(c.Namespace).Create(&svc)
if err != nil {
return nil, errors.Wrapf(err, "unable to create Service for %s", commonObjectMeta.Name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is wrapping error is necessary here?? IMO we should return bare errors from client functions, as they are intended to be called my multiple functions and it helps in checking conditions based on error codes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm following the error format for all the other functions under pkg/kclient (pod.go, volumes.go, deployments.go).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ya I see, resolving it for your PR :), will raise a separate concern.

@amitkrout
Copy link
Contributor

/test v4.1-integration-e2e-benchmark

@yangcao77
Copy link
Contributor Author

This PR currently blocks the work for #2592 which is targeted for sprint 180.
@amitkrout Can you help review this PR? Thanks.

@kadel
Copy link
Member

kadel commented Mar 11, 2020

/refresh

@openshift-ci-robot openshift-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. Required by Prow. label Mar 11, 2020
Copy link
Member

@kadel kadel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if two containers expose the same port it fails

▶ odo push
 •  Push devfile component spring-boot-http-booster  ...
 ✗  Failed to start component with name spring-boot-http-booster.
Error: Failed to start the component: unable to update Service for spring-boot-http-booster: Service "spring-boot-http-booster" is invalid: [spec.ports[1].name: Duplicate value: "8080-tcp", spec.ports[1]: Duplicate value: core.ServicePort{Name:"", Protocol:"TCP", Port:8080, TargetPort:intstr.IntOrString{Type:0, IntVal:0, StrVal:""}, NodePort:0}]

devfile.yaml

apiVersion: 1.0.0
metadata:
  name: test
components:
  - type: dockerimage
    alias: tools
    image: quay.io/eclipse/che-java8-maven:nightly
    memoryLimit: 768Mi
    endpoints:
      - name: "8080/tcp"
        port: 8080
    mountSources: true
    volumes:
      - name: m2
        containerPath: /home/user/.m2
  - type: dockerimage
    alias: tools2
    image: quay.io/eclipse/che-java8-maven:nightly
    memoryLimit: 768Mi
    endpoints:
      - name: "8080/tcp"
        port: 8080
    mountSources: true
    volumes:
      - name: m2
        containerPath: /home/user/.m2
commands:
  - name: build
    actions:
      - type: exec
        component: tools
        command: "mvn clean install"
  - name: run 
    actions:
      - type: exec
        component: tools
        command: java-jar target/*.jar

@kadel
Copy link
Member

kadel commented Mar 11, 2020

if two containers expose the same port it fails

▶ odo push
 •  Push devfile component spring-boot-http-booster  ...
 ✗  Failed to start component with name spring-boot-http-booster.
Error: Failed to start the component: unable to update Service for spring-boot-http-booster: Service "spring-boot-http-booster" is invalid: [spec.ports[1].name: Duplicate value: "8080-tcp", spec.ports[1]: Duplicate value: core.ServicePort{Name:"", Protocol:"TCP", Port:8080, TargetPort:intstr.IntOrString{Type:0, IntVal:0, StrVal:""}, NodePort:0}]

devfile.yaml

apiVersion: 1.0.0
metadata:
  name: test
components:
  - type: dockerimage
    alias: tools
    image: quay.io/eclipse/che-java8-maven:nightly
    memoryLimit: 768Mi
    endpoints:
      - name: "8080/tcp"
        port: 8080
    mountSources: true
    volumes:
      - name: m2
        containerPath: /home/user/.m2
  - type: dockerimage
    alias: tools2
    image: quay.io/eclipse/che-java8-maven:nightly
    memoryLimit: 768Mi
    endpoints:
      - name: "8080/tcp"
        port: 8080
    mountSources: true
    volumes:
      - name: m2
        containerPath: /home/user/.m2
commands:
  - name: build
    actions:
      - type: exec
        component: tools
        command: "mvn clean install"
  - name: run 
    actions:
      - type: exec
        component: tools
        command: java-jar target/*.jar

I take this back 😇
The devfile that I used actually doesn't make sense, as all the dockerimage components are containers in one pod, and they can't expose same port.

/appprove

Signed-off-by: Stephanie <stephanie.cao@ibm.com>
@openshift-ci-robot openshift-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. Required by Prow. label Mar 11, 2020
resourceReqs := GetResourceReqs(comp)
container := kclient.GenerateContainer(*comp.Alias, *comp.Image, false, comp.Command, comp.Args, envVars, resourceReqs)
containers = append(containers, *container)
// Only components with aliases are considered because without an alias commands cannot reference them
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think you over wrote the master changes during rebase, this logic has been moved to common.utils.go GetSupportedComponents()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remove the comment as well, that comment is for func GetAliasedComponents()

} else {
_, err = a.Client.CreateDeployment(*deploymentSpec)
if err != nil {
return err
}
glog.V(3).Infof("Successfully created component %v", componentName)
_, err = a.Client.CreateService(objectMeta, *serviceSpec)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question about something you brought up before, are we creating only one service for all the components?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One service for all ports in devfile.

for _, endpoint := range endpoints {
name := strings.TrimSpace(util.GetDNS1123Name(strings.ToLower(*endpoint.Name)))
containerPorts = append(containerPorts, corev1.ContainerPort{
Name: name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to be trimmed to below 63 char? I had a similar issue for "vol name", I later found out not only Kubernetes resources but other names are also capped at 63. Can you pls confirm this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out. Tested that the max length for port name is 15 characters. Push a new commit to trim it down to 15 characters.

Comment on lines +95 to +101
for _, c := range deploymentSpec.Template.Spec.Containers {
if len(containerPorts) == 0 {
containerPorts = c.Ports
} else {
containerPorts = append(containerPorts, c.Ports...)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if multiple components have the same endpoint, are we appending the same thing twice here? What happens when that is passed to GenerateServiceSpec

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There cannot be multiple components have the same endpoint, see Tomas's comment:

all the dockerimage components are containers in one pod, and they can't expose same port.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh ok thx, We should see if push's devfile parse validation is doing it for the endpoints or else add it in 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tomas has already tested it:

▶ odo push
 •  Push devfile component spring-boot-http-booster  ...
 ✗  Failed to start component with name spring-boot-http-booster.
Error: Failed to start the component: unable to update Service for spring-boot-http-booster: Service "spring-boot-http-booster" is invalid: [spec.ports[1].name: Duplicate value: "8080-tcp", spec.ports[1]: Duplicate value: core.ServicePort{Name:"", Protocol:"TCP", Port:8080, TargetPort:intstr.IntOrString{Type:0, IntVal:0, StrVal:""}, NodePort:0}]

@kadel
Copy link
Member

kadel commented Mar 12, 2020

/approve

@openshift-ci-robot
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: kadel

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci-robot openshift-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. Required by Prow. label Mar 12, 2020
Signed-off-by: Stephanie <stephanie.cao@ibm.com>
Signed-off-by: Stephanie <stephanie.cao@ibm.com>
for _, endpoint := range endpoints {
name := strings.TrimSpace(util.GetDNS1123Name(strings.ToLower(*endpoint.Name)))
if len(name) > 15 {
name = name[:15]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use the util function util.TruncateString instead here and have a const for 15

resourceReqs := GetResourceReqs(comp)
container := kclient.GenerateContainer(*comp.Alias, *comp.Image, false, comp.Command, comp.Args, envVars, resourceReqs)
containers = append(containers, *container)
// Only components with aliases are considered because without an alias commands cannot reference them
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remove the comment as well, that comment is for func GetAliasedComponents()

// generate Service Spec
var svcPorts []corev1.ServicePort
for _, containerPort := range containerPorts {
svcPort := corev1.ServicePort{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're reconverting from corev1.ContainerPort to corev1.ServicePort, can we just use corev1.ServicePort in utils.go ConvertPorts()? Then your GenerateServiceSpec() can just take corev1.ServicePort directly and does not need to convert

Copy link
Contributor Author

@yangcao77 yangcao77 Mar 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pod spec need it to be in format of corev1.ContainerPort when we generate it use the containers returned from utils.GetContainers()

if err != nil {
return err
}
glog.V(3).Infof("Successfully created component %v", componentName)
_, err = a.Client.CreateService(objectMeta, *serviceSpec)
ownerReference := kclient.GenerateOwnerReference(deployment)
objectMetaTemp := objectMeta
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why you have an objectMetaTemp for update, but i think you dont really need it for create..

Spec: svcSpec,
}

service, err := c.KubeClient.CoreV1().Services(c.Namespace).Create(&svc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I tried odo push with multiple components having the same endpoints, and i noticed that there is no devfile validation..

Services.Create() fails because it cannot create service with two same endpoints, but this results in a deployment and a pod that is left hanging because it could not create pvc which is after service creation.

NAME                                    READY   STATUS    RESTARTS   AGE   LABELS
pod/spring-devfile-85d64fdd5b-dxn4j     0/2     Pending   0          9s    component=spring-devfile,pod-template-hash=4182098816

NAME                                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE   LABELS
deployment.extensions/spring-devfile     1         1         1            0           9s    component=spring-devfile

Maybe we should validate the data in devfile before we do odo push but I think we dont have it now. Could be done later with an another PR perhaps, if we want to do it...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added validation for duplicate ports

@openshift-ci-robot openshift-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. Required by Prow. label Mar 16, 2020
Signed-off-by: Stephanie <stephanie.cao@ibm.com>
Signed-off-by: Stephanie <stephanie.cao@ibm.com>
@openshift-ci-robot openshift-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. Required by Prow. label Mar 16, 2020
@yangcao77
Copy link
Contributor Author

/test v4.3-integration-e2e-benchmark
/test v4.2-integration-e2e-benchmark
/test v4.1-integration-e2e-benchmark

1 similar comment
@yangcao77
Copy link
Contributor Author

/test v4.3-integration-e2e-benchmark
/test v4.2-integration-e2e-benchmark
/test v4.1-integration-e2e-benchmark

@kadel
Copy link
Member

kadel commented Mar 18, 2020

/test v4.3-integration-e2e-benchmark
/test v4.2-integration-e2e-benchmark
/test v4.1-integration-e2e-benchmark

@yangcao77 you can save some typing using /retest
This command automatically restarts all failed Prow tests ;-)

/retest

@yangcao77
Copy link
Contributor Author

/test v4.2-integration-e2e-benchmark

2 similar comments
@yangcao77
Copy link
Contributor Author

/test v4.2-integration-e2e-benchmark

@yangcao77
Copy link
Contributor Author

/test v4.2-integration-e2e-benchmark

@kadel
Copy link
Member

kadel commented Mar 19, 2020

/retest

@maysunfaisal
Copy link
Contributor

Thx for the change, works well 👍

/lgtm

@openshift-ci-robot openshift-ci-robot added the lgtm Indicates that a PR is ready to be merged. Required by Prow. label Mar 19, 2020
@openshift-bot
Copy link

/retest

Please review the full test history for this PR and help us cut down flakes.

2 similar comments
@openshift-bot
Copy link

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-bot
Copy link

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-merge-robot openshift-merge-robot merged commit f940c55 into redhat-developer:master Mar 20, 2020
@rm3l rm3l added estimated-size/L (20-40) Rough sizing for Epics. About 2 sprints of work for a person. and removed size/L labels Jun 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. Required by Prow. estimated-size/L (20-40) Rough sizing for Epics. About 2 sprints of work for a person. kind/feature Categorizes issue as a feature request. For PRs, that means that the PR is the implementation lgtm Indicates that a PR is ready to be merged. Required by Prow. ok-to-test Indicates a non-member PR verified by an org member that is safe to test.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants