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

pkg/generator: Generate all manifests needed for Prometheus integration #241

Closed
wants to merge 25 commits into from

Conversation

etiennecoutaud
Copy link
Contributor

Refer to #222

Generate kubernetes manifest files to integrate operator with Prometheus

@etiennecoutaud
Copy link
Contributor Author

cc @spahl

spec:
replicas: 1
selector:
matchLabels:
name: {{.ProjectName}}
k8s-app: {{.ProjectName}}
Copy link
Contributor

Choose a reason for hiding this comment

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

why change name to k8s-app?

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 introduced k8s-app label for service and servicemonitor. I just changed to have the same label everywhere.

If needed I can change to use name label in service and servicemonitor

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should keep the the original label name for now. if change is indeed needed, we can change the label to something else in a future pr.

cc/ @spahl @hasbro17 on label change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay for using name label.
Should I use name label in service and servicemonitor too ?

@fanminshi
Copy link
Contributor

Could you update the commit message title format as shown in https://github.com/operator-framework/operator-sdk/blob/master/CONTRIBUTING.MD#format-of-the-commit-message?

@etiennecoutaud etiennecoutaud changed the title Prometheus integration with serviceMonitor pkg/generator: Generate all manifests needed for Prometheus integration May 15, 2018
spec:
containers:
- name: {{.ProjectName}}
image: {{.Image}}
ports:
- containerPort: 9090
Copy link
Contributor

Choose a reason for hiding this comment

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

can we name this port? and then use that in the service as targetPort: name. That leaves us flexibility to change the port in the future without having to change the service.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure ! 👍

Copy link
Contributor Author

@etiennecoutaud etiennecoutaud May 16, 2018

Choose a reason for hiding this comment

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

containerPort is now name metrics and use in the service as targetPort: metrics

Copy link
Contributor

Choose a reason for hiding this comment

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

Make the port number a variable like {{.MetricsPort}} and define a new parameter in gen_deploy.go

type OperatorYaml struct {
	ProjectName string
	Image       string
	MetricsPort int
}

That way we can set MetricsPort to a constant defined elsewhere.

@etiennecoutaud
Copy link
Contributor Author

finally change all k8s-app labels to name

@eparis
Copy link
Contributor

eparis commented May 16, 2018

This PR LGTM, though it does bother me a little that we have "9090" hard coded in 4 places. Not sure it's worth putting in the template data, but if we really wanted to do a followup.

@etiennecoutaud
Copy link
Contributor Author

etiennecoutaud commented May 16, 2018

I agree with you, an another way could be to add a config file pass to the generator when operator-sdk new is running.
We could enable or not prometheus integration, define metrics ports, git init etc ...
something like

$ cat operator-sdk.cfg
prometheus_integration = enabled
metrics_port = 9090
git_init = true

$ operator-sdk new app-operator --api-version=app.example.com/v1alpha1 --kind=App --config=operator-sdk.cfg

@eparis
Copy link
Contributor

eparis commented May 16, 2018

I'd personally lean pretty hard on 'only give users 1 way to do it and make sure that way is the right way'. I'm definitely in favor of us putting the port into the code and not making it optional. My only (tiny) bit of unease is that it's in the code 4 times instead of 1.

Fewer options make users less able to get into weird situations we don't test or aren't able to help them with.

@chancez
Copy link
Contributor

chancez commented May 16, 2018

I feel like hard-coding ports is going to a problem if you ever need to run another program that could use that port as a side-car to the operator.

@etiennecoutaud
Copy link
Contributor Author

In that case, we cannot prevent futur usage , so user will have to change promport in main.go and metrics port in manifest files

@hasbro17
Copy link
Contributor

We should follow up with the discussion at #222 (comment) before we decide if we want to move forward with this approach.

@etiennecoutaud
Copy link
Contributor Author

Update done according to #222

"github.com/sirupsen/logrus"
)

// Prometheus metrics port
const promPort = ":9090"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use a less common port number at a higher range like 90000.
Also can you define the port number as a global constant somewhere other than the test file like in k8sutil/constants.go. That way we can use it everywhere in the generator package without redefining it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Or something that's actually in the ephemeral port range like 60K. 90K is out of range.

@@ -54,6 +59,10 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: OPERATOR_NAME
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly can you make the OPERATOR_NAME and WATCH_NAMESPACE strings as variables in the template so we can set them using the namespace constants defined in k8sutil/constants.go.

type OperatorYaml struct {
	ProjectName string
	Image       string
        NameEnv  string
        NamespaceEnv  string
	MetricsPort int
}

@etiennecoutaud
Copy link
Contributor Author

@hasbro17 according to your review:

  • constant MetricsPort is defined in k8sutil/constant and set to 60000
  • OperatorYaml struct has been updated to embedded environment vars value defined in k8sutil/constant
  • Main struct has also been updated with MetricsPort value from constant to use it in template

@@ -529,14 +551,19 @@ spec:
containers:
- name: {{.ProjectName}}
image: {{.Image}}
ports:
- containerPort: {{.MetricsPort}}
name: metrics
Copy link
Contributor

Choose a reason for hiding this comment

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

The string metrics is being used here and in InitOperatorService().
We should make this a constant in k8sutil/constants.go.
And then pass it as a parameter name: {{.MetricsPortName}}.

@@ -55,6 +58,7 @@ func renderMainFile(w io.Writer, repo, apiVersion, kind string) error {
SDKVersionImport: versionImport,
APIVersion: apiVersion,
Kind: kind,
MetricsPort: k8sutil.GetPrometheusMetricsPort(),
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need to convert this to a string. We can just use k8sutil.PrometheusMetricsPort.
And in the template down below we can change it to:

go http.ListenAndServe(":{{.MetricsPort}}", nil)

Gopkg.toml Outdated
@@ -6,6 +6,10 @@
name = "github.com/spf13/cobra"
version = "0.0.2"

[[override]]
name = "github.com/prometheus/client_golang"
Copy link
Contributor

Choose a reason for hiding this comment

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

This package isn't really used in the SDK itself right now so we can remove this. We'll add it back in later once we actually use this to record default metrics inside the SDK.
For now we can add it to the Gopkg.toml template of the operator:
https://github.com/operator-framework/operator-sdk/blob/master/pkg/generator/templates.go#L372

sdk "github.com/operator-framework/operator-sdk/pkg/sdk"
k8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
sdkVersion "github.com/operator-framework/operator-sdk/version"
stub "github.com/example-inc/app-operator/pkg/stub"
Copy link
Contributor

Choose a reason for hiding this comment

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

We should keep the tab size to 4 as it is for all the other templates.

sdk "{{.OperatorSDKImport}}"
k8sutil "{{.K8sutilImport}}"
sdkVersion "{{.SDKVersionImport}}"
stub "{{.StubImport}}"
Copy link
Contributor

Choose a reason for hiding this comment

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

Change tab size back to 4

}
err = sdk.Create(service)
if err != nil {
logrus.Infof("Failed to create operator service: %v", err)
Copy link
Contributor

@hasbro17 hasbro17 Jun 13, 2018

Choose a reason for hiding this comment

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

We should ignore the error if the service already exists as it's very likely that the operator pod can restart.

import "k8s.io/apimachinery/pkg/api/errors"
...
if err != nil && !errors.IsAlreadyExists(err) {
    logrus.Infof("Failed to create operator service: %v", err)
    return
}

logrus.Infof("Failed to create operator service: %v", err)
return
}
logrus.Infof("Service %s have been created", service.Name)
Copy link
Contributor

Choose a reason for hiding this comment

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

logrus.Infof("Metrics service %s created", service.Name)

@hasbro17
Copy link
Contributor

@etiennecoutaud Thanks for the update. I've tested this out locally and it works fine.
If you can address the above changes we should be good. Also you'll need to rebase since we refactored the generator in #310

@etiennecoutaud
Copy link
Contributor Author

etiennecoutaud commented Jun 15, 2018

@hasbro17 I completley failed my rebase 😄, anyway the refactor was too heavy. I will close this PR and reopen a clean one.

Is it normally that generator refactor remove all unit tests ?

@hasbro17
Copy link
Contributor

@etiennecoutaud No that was my mistake. We removed them by accident as part of the refactor. We're going to revert that #315

@etiennecoutaud
Copy link
Contributor Author

etiennecoutaud commented Jun 15, 2018

No problem, I close this PR and wait for #315.

@hasbro17
Copy link
Contributor

@etiennecoutaud Can you reopen this in another PR. I think #315 will only change the main template unit test which should be easy to rebase once it's merged.

Also looking back I think it's best if we keep the generated code to a minimum and keep the port exposing and service creation utility in the SDK.

So the generated main.go should look like:

func main() {
	printVersion()

	sdk.ExposeMetricsPort()

	resource := "app.example.com/v1alpha1"
	kind := "App"
	namespace, err := k8sutil.GetWatchNamespace()
	if err != nil {
		logrus.Fatalf("Failed to get watch namespace: %v", err)
	}
	resyncPeriod := 5
	logrus.Infof("Watching %s, %s, %s, %d", resource, kind, namespace, resyncPeriod)
	sdk.Watch(resource, kind, namespace, resyncPeriod)
	sdk.Handle(stub.NewHandler())
	sdk.Run(context.TODO())
}

And sdk.ExposeMetricsPort() can be defined in pkg/sdk/metrics.go as:

func ExposeMetricsPort() {
  http.Handle("/"+k8sutil.PrometheusMetricsPortName, promhttp.Handler())
  go http.ListenAndServe(":"+k8sutil. PrometheusMetricsPort, nil)

  service, err := k8sutil.InitOperatorService()
  if err != nil {
    logrus.Fatalf("Failed to init operator service: %v", err)
  }
  err = sdk.Create(service)
  if err != nil && !errors.IsAlreadyExists(err) {
    logrus.Infof("Failed to create operator service: %v", err)
    return
  }
  logrus.Infof("Metrics service %s created", service.Name)
}

This way we can actually use the constants for the metrics port and not have them generated as a number and string in the operator's main.go. It also keeps main.go small.
Later on we can change sdk.ExposeMetricsPort() to be configurable by taking the port number as an arg if needed.

@etiennecoutaud
Copy link
Contributor Author

Yes sure, I will before the end of the week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants