Skip to content

Commit

Permalink
pkg/kube-metrics: Add initial operator specific metrics
Browse files Browse the repository at this point in the history
Operator specific metrics are generated based on the api/kind given
deployed in the given namespace(s).
  • Loading branch information
lilic committed Apr 2, 2019
1 parent 83f710b commit 8b5f899
Show file tree
Hide file tree
Showing 20 changed files with 1,331 additions and 40 deletions.
21 changes: 21 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
name = "github.com/spf13/cobra"
version = "0.0.3"

[[constraint]]
name = "k8s.io/kube-state-metrics"
branch = "master"

# We need overrides for the following imports because dep can't resolve them
# correctly. The easiest way to get this right is to use the versions that
# k8s.io/helm uses. See https://github.com/helm/helm/blob/v2.13.1/glide.lock
Expand Down
1 change: 1 addition & 0 deletions cmd/operator-sdk/add/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func apiRun(cmd *cobra.Command, args []string) error {
&scaffold.Doc{Resource: r},
&scaffold.CR{Resource: r},
&scaffold.CRD{Resource: r, IsOperatorGo: projutil.IsOperatorGo()},
&scaffold.Metrics{Resource: r},
)
if err != nil {
return fmt.Errorf("api scaffold failed: (%v)", err)
Expand Down
11 changes: 9 additions & 2 deletions internal/pkg/scaffold/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import (
"{{ .Repo }}/pkg/apis"
"{{ .Repo }}/pkg/controller"
operatormetrics "{{ .Repo }}/pkg/metrics"
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
"github.com/operator-framework/operator-sdk/pkg/leader"
Expand All @@ -63,8 +64,9 @@ import (
// Change below variables to serve metrics on different host or port.
var (
metricsHost = "0.0.0.0"
metricsPort int32 = 8383
metricsHost = "0.0.0.0"
metricsPort int32 = 8383
operatorMetricsPort int32 = 8686
)
var log = logf.Log.WithName("cmd")
Expand Down Expand Up @@ -112,6 +114,11 @@ func main() {
ctx := context.TODO()
// Start serving operator specific metrics.
if err := operatormetrics.ServeOperatorSpecificMetrics(cfg, metricsHost, operatorMetricsPort); err != nil {
log.Error(err, "")
}
// Become the leader before proceeding
err = leader.Become(ctx, "{{ .ProjectName }}-lock")
if err != nil {
Expand Down
11 changes: 9 additions & 2 deletions internal/pkg/scaffold/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
"github.com/example-inc/app-operator/pkg/apis"
"github.com/example-inc/app-operator/pkg/controller"
operatormetrics "github.com/example-inc/app-operator/pkg/metrics"
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
"github.com/operator-framework/operator-sdk/pkg/leader"
Expand All @@ -62,8 +63,9 @@ import (
// Change below variables to serve metrics on different host or port.
var (
metricsHost = "0.0.0.0"
metricsPort int32 = 8383
metricsHost = "0.0.0.0"
metricsPort int32 = 8383
operatorMetricsPort int32 = 8686
)
var log = logf.Log.WithName("cmd")
Expand Down Expand Up @@ -111,6 +113,11 @@ func main() {
ctx := context.TODO()
// Start serving operator specific metrics.
if err := operatormetrics.ServeOperatorSpecificMetrics(cfg, metricsHost, operatorMetricsPort); err != nil {
log.Error(err, "")
}
// Become the leader before proceeding
err = leader.Become(ctx, "app-operator-lock")
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions internal/pkg/scaffold/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ const (
OLMCatalogDir = DeployDir + filePathSep + "olm-catalog"
CRDsDir = DeployDir + filePathSep + "crds"
VersionDir = "version"
MetricsDir = PkgDir + filePathSep + "metrics"
)
8 changes: 8 additions & 0 deletions internal/pkg/scaffold/gopkgtoml.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ required = [
name = "github.com/coreos/prometheus-operator"
version = "=v0.26.0"
[[constraint]]
name = "k8s.io/kube-state-metrics"
branch = "master"
[[override]]
name = "sigs.k8s.io/controller-runtime"
version = "=v0.1.10"
Expand All @@ -109,6 +113,10 @@ required = [
go-tests = true
non-go = true
[[prune.project]]
name = "k8s.io/kube-state-metrics"
unused-packages = true
[[prune.project]]
name = "k8s.io/code-generator"
non-go = false
Expand Down
8 changes: 8 additions & 0 deletions internal/pkg/scaffold/gopkgtoml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ required = [
name = "github.com/coreos/prometheus-operator"
version = "=v0.26.0"
[[constraint]]
name = "k8s.io/kube-state-metrics"
branch = "master"
[[override]]
name = "sigs.k8s.io/controller-runtime"
version = "=v0.1.10"
Expand All @@ -101,6 +105,10 @@ required = [
go-tests = true
non-go = true
[[prune.project]]
name = "k8s.io/kube-state-metrics"
unused-packages = true
[[prune.project]]
name = "k8s.io/code-generator"
non-go = false
Expand Down
96 changes: 96 additions & 0 deletions internal/pkg/scaffold/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2019 The Operator-SDK Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package scaffold

import (
"path/filepath"

"github.com/operator-framework/operator-sdk/internal/pkg/scaffold/input"
)

const MetricsFile = "metrics.go"

type Metrics struct {
input.Input

// Resource defines the inputs for the new custom resource definition
Resource *Resource
}

func (s *Metrics) GetInput() (input.Input, error) {
if s.Path == "" {
s.Path = filepath.Join(MetricsDir, MetricsFile)
}
s.TemplateBody = metricsPkgTemplate
return s.Input, nil
}

const metricsPkgTemplate = `package metrics
import (
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/rest"
ksmetric "k8s.io/kube-state-metrics/pkg/metric"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
)
var log = logf.Log.WithName("metrics")
var resource = "{{ .Resource.APIVersion }}"
var kind = "{{ .Resource.Kind }}"
var metricName = "{{ .Resource.LowerKind }}_info"
var (
MetricFamilies = []ksmetric.FamilyGenerator{
ksmetric.FamilyGenerator{
Name: metricName,
Type: ksmetric.Gauge,
Help: "Information about the {{ .Resource.Kind }} operator replica.",
GenerateFunc: func(obj interface{}) *ksmetric.Family {
crd := obj.(*unstructured.Unstructured)
return &ksmetric.Family{
Metrics: []*ksmetric.Metric{
{
Value: 1,
LabelKeys: []string{"namespace", "{{ .Resource.LowerKind }}"},
LabelValues: []string{crd.GetNamespace(), crd.GetName()},
},
},
}
},
},
}
)
func ServeOperatorSpecificMetrics(cfg *rest.Config, host string, port int32) error {
uc := kubemetrics.NewForConfig(cfg)
// By default the current namespace will be detected and used to create metrics.
// Add to the namespaces to include any other namespaces.
namespaces := []string{}
c, err := kubemetrics.NewCollector(uc, namespaces, resource, kind, MetricFamilies)
if err != nil {
if err == k8sutil.ErrNoNamespace {
log.Info("Skipping operator specific metrics; not running in a cluster.")
return nil
}
return err
}
go kubemetrics.ServeMetrics(c, host, port)
return nil
}
`
96 changes: 96 additions & 0 deletions internal/pkg/scaffold/metrics_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2019 The Operator-SDK Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package scaffold

import (
"testing"

"github.com/operator-framework/operator-sdk/internal/util/diffutil"
)

func TestMetrics(t *testing.T) {
r, err := NewResource(appApiVersion, appKind)
if err != nil {
t.Fatal(err)
}
s, buf := setupScaffoldAndWriter()
err = s.Execute(appConfig, &Metrics{Resource: r})
if err != nil {
t.Fatalf("Failed to execute the scaffold: (%v)", err)
}

if metricsExp != buf.String() {
diffs := diffutil.Diff(metricsExp, buf.String())
t.Fatalf("Expected vs actual differs.\n%v", diffs)
}
}

const metricsExp = `package metrics
import (
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/rest"
ksmetric "k8s.io/kube-state-metrics/pkg/metric"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
)
var log = logf.Log.WithName("metrics")
var resource = "app.example.com/v1alpha1"
var kind = "AppService"
var metricName = "appservice_info"
var (
MetricFamilies = []ksmetric.FamilyGenerator{
ksmetric.FamilyGenerator{
Name: metricName,
Type: ksmetric.Gauge,
Help: "Information about the AppService operator replica.",
GenerateFunc: func(obj interface{}) *ksmetric.Family {
crd := obj.(*unstructured.Unstructured)
return &ksmetric.Family{
Metrics: []*ksmetric.Metric{
{
Value: 1,
LabelKeys: []string{"namespace", "appservice"},
LabelValues: []string{crd.GetNamespace(), crd.GetName()},
},
},
}
},
},
}
)
func ServeOperatorSpecificMetrics(cfg *rest.Config, host string, port int32) error {
uc := kubemetrics.NewForConfig(cfg)
// By default the current namespace will be detected and used to create metrics.
// Add to the namespaces to include any other namespaces.
namespaces := []string{}
c, err := kubemetrics.NewCollector(uc, namespaces, resource, kind, MetricFamilies)
if err != nil {
if err == k8sutil.ErrNoNamespace {
log.Info("Skipping operator specific metrics; not running in a cluster.")
return nil
}
return err
}
go kubemetrics.ServeMetrics(c, host, port)
return nil
}
`
Loading

0 comments on commit 8b5f899

Please sign in to comment.