Skip to content

Commit

Permalink
pkg/{sdk,generator} Expose Prometheus metrics port
Browse files Browse the repository at this point in the history
  • Loading branch information
etiennecoutaud committed Jun 22, 2018
1 parent 603eb71 commit 55bc84f
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 5 deletions.
16 changes: 12 additions & 4 deletions pkg/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"path/filepath"
"strings"
"text/template"

k8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
)

const (
Expand Down Expand Up @@ -237,8 +239,11 @@ func renderDeployFiles(deployDir, projectName, apiVersion, kind string) error {
// RenderOperatorYaml generates "deploy/operator.yaml"
func RenderOperatorYaml(c *Config, image string) error {
td := tmplData{
ProjectName: c.ProjectName,
Image: image,
ProjectName: c.ProjectName,
Image: image,
MetricsPort: k8sutil.PrometheusMetricsPort,
MetricsPortName: k8sutil.PrometheusMetricsPortName,
OperatorNameEnv: k8sutil.OperatorNameEnvVar,
}
return renderWriteFile(operatorYaml, operatorTmplName, operatorYamlTmpl, td)
}
Expand Down Expand Up @@ -443,8 +448,11 @@ type tmplData struct {
// plural name to be used in the URL: /apis/<group>/<version>/<plural>
KindPlural string

Image string
Name string
Image string
Name string
MetricsPort int
MetricsPortName string
OperatorNameEnv string

PackageName string
ChannelName string
Expand Down
11 changes: 10 additions & 1 deletion pkg/generator/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"path/filepath"
"strings"
"testing"

k8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
)

const updateGeneratedExp = `#!/usr/bin/env bash
Expand Down Expand Up @@ -193,6 +195,9 @@ spec:
containers:
- name: app-operator
image: quay.io/example-inc/app-operator:0.0.1
ports:
- containerPort: 60000
name: metrics
command:
- app-operator
imagePullPolicy: Always
Expand All @@ -201,6 +206,8 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: OPERATOR_NAME
value: "app-operator"
`

const rbacYamlExp = `kind: Role
Expand Down Expand Up @@ -268,7 +275,7 @@ func TestGenDeploy(t *testing.T) {
}

buf = &bytes.Buffer{}
if err := renderFile(buf, operatorTmplName, operatorYamlTmpl, tmplData{ProjectName: appProjectName, Image: appImage}); err != nil {
if err := renderFile(buf, operatorTmplName, operatorYamlTmpl, tmplData{ProjectName: appProjectName, Image: appImage, MetricsPort: k8sutil.PrometheusMetricsPort, MetricsPortName: k8sutil.PrometheusMetricsPortName, OperatorNameEnv: k8sutil.OperatorNameEnvVar}); err != nil {
t.Error(err)
}
if operatorYamlExp != buf.String() {
Expand Down Expand Up @@ -407,6 +414,8 @@ func printVersion() {
func main() {
printVersion()
sdk.ExposeMetricsPort()
resource := "app.example.com/v1alpha1"
kind := "AppService"
namespace, err := k8sutil.GetWatchNamespace()
Expand Down
7 changes: 7 additions & 0 deletions pkg/generator/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ func printVersion() {
func main() {
printVersion()
sdk.ExposeMetricsPort()
resource := "{{.APIVersion}}"
kind := "{{.Kind}}"
namespace, err := k8sutil.GetWatchNamespace()
Expand Down Expand Up @@ -417,6 +419,9 @@ spec:
containers:
- name: {{.ProjectName}}
image: {{.Image}}
ports:
- containerPort: {{.MetricsPort}}
name: {{.MetricsPortName}}
command:
- {{.ProjectName}}
imagePullPolicy: Always
Expand All @@ -425,6 +430,8 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: {{.OperatorNameEnv}}
value: "{{.ProjectName}}"
`

const rbacYamlTmpl = `kind: Role
Expand Down
28 changes: 28 additions & 0 deletions pkg/sdk/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package sdk

import (
"net/http"
"strconv"

k8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/errors"
)

// ExposeMetricsPort generate a Kubernetes Service to expose metrics port
func ExposeMetricsPort() {
http.Handle("/"+k8sutil.PrometheusMetricsPortName, promhttp.Handler())
go http.ListenAndServe(":"+strconv.Itoa(k8sutil.PrometheusMetricsPort), nil)

service, err := k8sutil.InitOperatorService()
if err != nil {
logrus.Fatalf("Failed to init operator service: %v", err)
}
err = 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)
}
10 changes: 10 additions & 0 deletions pkg/util/k8sutil/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,14 @@ const (
// WatchNamespaceEnvVar is the constant for env variable WATCH_NAMESPACE
// which is the namespace that the pod is currently running in.
WatchNamespaceEnvVar = "WATCH_NAMESPACE"

// OperatorNameEnvVar is the constant for env variable OPERATOR_NAME
// wich is the name of the current operator
OperatorNameEnvVar = "OPERATOR_NAME"

// PrometheusMetricsPort defines the port which expose prometheus metrics
PrometheusMetricsPort = 60000

// PrometheusMetricsPortName define the port name used in kubernetes deployment and service
PrometheusMetricsPortName = "metrics"
)
52 changes: 52 additions & 0 deletions pkg/util/k8sutil/k8sutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ import (
"fmt"
"os"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
intstr "k8s.io/apimachinery/pkg/util/intstr"
cgoscheme "k8s.io/client-go/kubernetes/scheme"
)

Expand Down Expand Up @@ -147,3 +149,53 @@ func GetWatchNamespace() (string, error) {
}
return ns, nil
}

// GetOperatorName return the operator name
func GetOperatorName() (string, error) {
operatorName, found := os.LookupEnv(OperatorNameEnvVar)
if !found {
return "", fmt.Errorf("%s must be set", OperatorNameEnvVar)
}
if len(operatorName) == 0 {
return "", fmt.Errorf("%s must not be empty", OperatorNameEnvVar)
}
return operatorName, nil
}

// InitOperatorService return the static service which expose operator metrics
func InitOperatorService() (*v1.Service, error) {
operatorName, err := GetOperatorName()
if err != nil {
return nil, err
}
namespace, err := GetWatchNamespace()
if err != nil {
return nil, err
}
service := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: operatorName,
Namespace: namespace,
Labels: map[string]string{"name": operatorName},
},
TypeMeta: metav1.TypeMeta{
Kind: "Service",
APIVersion: "v1",
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{
{
Port: PrometheusMetricsPort,
Protocol: v1.ProtocolTCP,
TargetPort: intstr.IntOrString{
Type: intstr.String,
StrVal: PrometheusMetricsPortName,
},
Name: PrometheusMetricsPortName,
},
},
Selector: map[string]string{"name": operatorName},
},
}
return service, nil
}
61 changes: 61 additions & 0 deletions pkg/util/k8sutil/k8sutil_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package k8sutil

import (
"fmt"
"os"
"reflect"
"testing"
)

func TestGetOperatorName(t *testing.T) {
type Output struct {
operatorName string
err error
}

type Scenario struct {
name string
envVarKey string
envVarValue string
expectedOutput Output
}

tests := []Scenario{
Scenario{
name: "Simple case",
envVarKey: OperatorNameEnvVar,
envVarValue: "myoperator",
expectedOutput: Output{
operatorName: "myoperator",
err: nil,
},
},
Scenario{
name: "Unset env var",
envVarKey: "",
envVarValue: "",
expectedOutput: Output{
operatorName: "",
err: fmt.Errorf("%s must be set", OperatorNameEnvVar),
},
},
Scenario{
name: "Empty env var",
envVarKey: OperatorNameEnvVar,
envVarValue: "",
expectedOutput: Output{
operatorName: "",
err: fmt.Errorf("%s must not be empty", OperatorNameEnvVar),
},
},
}

for _, test := range tests {
_ = os.Setenv(test.envVarKey, test.envVarValue)
operatorName, err := GetOperatorName()
if !(operatorName == test.expectedOutput.operatorName && reflect.DeepEqual(err, test.expectedOutput.err)) {
t.Errorf("test %s failed, expected ouput: %s,%v; got: %s,%v", test.name, test.expectedOutput.operatorName, test.expectedOutput.err, operatorName, err)
}
_ = os.Unsetenv(test.envVarKey)
}
}

0 comments on commit 55bc84f

Please sign in to comment.