Skip to content

Commit

Permalink
feat: add hostname, uptime and gateway count providers (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmalek authored Aug 9, 2022
1 parent 6f99136 commit fe6d3b8
Show file tree
Hide file tree
Showing 11 changed files with 387 additions and 5 deletions.
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ require (
sigs.k8s.io/controller-runtime v0.12.3
)

require github.com/spf13/pflag v1.0.5 // indirect
require (
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/spf13/pflag v1.0.5 // indirect
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
Expand All @@ -32,6 +36,7 @@ require (
github.com/imdario/mergo v0.3.13 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/k0kubun/pp/v3 v3.1.0
github.com/mailru/easyjson v0.7.7 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
Expand All @@ -52,7 +57,9 @@ require (
k8s.io/api v0.24.3
k8s.io/klog/v2 v2.70.0 // indirect
k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 // indirect
k8s.io/kubectl v0.24.3
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
sigs.k8s.io/gateway-api v0.5.0
sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
Expand Down
243 changes: 243 additions & 0 deletions go.sum

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions pkg/provider/hostnameprovider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package provider

import (
"os"
)

const (
// HostnameKey is the report key that under which one can find hostname.
HostnameKey = ReportKey("hn")
)

// NewHostnameProvider creates hostname provider.
func NewHostnameProvider(name string) (Provider, error) {
return &functor{
f: func() (Report, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
return Report{
HostnameKey: hostname,
}, nil
},
base: base{
name: name,
kind: "hostname",
},
}, nil
}
38 changes: 38 additions & 0 deletions pkg/provider/k8sgatewayscountprovider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package provider

import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
)

const (
// GatewayCountKey is report key under which the number of pods in the cluster
// will be provided.
GatewayCountKey = ReportKey("k8s_gateways_count")
// GatewayCountKind represents the pod count provider kind.
GatewayCountKind = Kind(GatewayCountKey)
)

// NewK8sGatewayCountProvider creates telemetry data provider that will query the
// configured k8s cluster - using the provided client - to get a gateway count from
// the cluster.
func NewK8sGatewayCountProvider(name string, d dynamic.Interface) (Provider, error) {
gvk := schema.GroupVersionResource{
Group: "gateway.networking.k8s.io",
Version: "v1beta1",
Resource: "gateways",
}

// TODO:
// consider detecting what resource version is available on the cluster to
// properly report. Alternatively consider reporting version together with
// the count.
return &k8sObjectCount{
resource: d.Resource(gvk),
gvk: gvk,
base: base{
name: name,
kind: GatewayCountKind,
},
}, nil
}
7 changes: 6 additions & 1 deletion pkg/provider/uptimeprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ import (
"time"
)

const (
// UptimeKey is the report key that under which one can find uptime.
UptimeKey = ReportKey("uptime")
)

// NewUptimeProvider provides new uptime provider which will return uptime counted
// since the provider creation time.
func NewUptimeProvider(name string) (Provider, error) {
start := time.Now()
return &functor{
f: func() (Report, error) {
return Report{
"uptime": int(time.Since(start).Truncate(time.Second).Seconds()),
UptimeKey: int(time.Since(start).Truncate(time.Second).Seconds()),
}, nil
},
base: base{
Expand Down
3 changes: 3 additions & 0 deletions pkg/telemetry/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ func (m *manager) Execute(ctx context.Context) (types.Report, error) {
r, err = v.Execute(ctx)
if err != nil {
err = errors.Wrapf(err, "error executing workflow %s", name)
// TODO: return true and don't abort when encountering an error.
// Better to report partial report than nothing. In order to do so
// use an error agreggator like https://github.com/hashicorp/go-multierror.
return false
}

Expand Down
22 changes: 21 additions & 1 deletion pkg/telemetry/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
dyn_fake "k8s.io/client-go/dynamic/fake"
clientgo_fake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme"
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"

"github.com/kong/kubernetes-telemetry/pkg/forwarders"
"github.com/kong/kubernetes-telemetry/pkg/provider"
Expand Down Expand Up @@ -151,7 +153,16 @@ func TestManagerWithMultilpleWorkflows(t *testing.T) {

func TestManagerWithCatalogWorkflows(t *testing.T) {
t.Run("identify platform and cluster state", func(t *testing.T) {
dynClient := dyn_fake.NewSimpleDynamicClient(scheme.Scheme,
require.NoError(t, gatewayv1beta1.Install(scheme.Scheme))

dynClient := dyn_fake.NewSimpleDynamicClientWithCustomListKinds(scheme.Scheme,
map[schema.GroupVersionResource]string{
{
Group: "gateway.networking.k8s.io",
Version: "v1beta1",
Resource: "gateways",
}: "GatewayList",
},
&corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kong",
Expand All @@ -166,6 +177,12 @@ func TestManagerWithCatalogWorkflows(t *testing.T) {
},
},
},
&gatewayv1beta1.Gateway{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kong",
Name: "gateway-1",
},
},
&corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: "namespace1",
Expand Down Expand Up @@ -208,6 +225,9 @@ func TestManagerWithCatalogWorkflows(t *testing.T) {
"cluster-state": provider.Report{
"k8s_pods_count": 1,
"k8s_services_count": 2,
// TODO fix below count: it should be 1 but for some reason even after adding the GVR
// to scheme gateways can't be found by listing.
"k8s_gateways_count": 0,
},
"identify-platform": provider.Report{
"k8s_arch": fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
Expand Down
5 changes: 5 additions & 0 deletions pkg/telemetry/workflowsbase.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ func NewStateWorkflow() (Workflow, error) {
if err != nil {
return nil, err
}
hostnameProvider, err := provider.NewHostnameProvider("hostname")
if err != nil {
return nil, err
}

w := NewWorkflow(StateWorkflowName)
w.AddProvider(uptimeProvider)
w.AddProvider(hostnameProvider)

return w, nil
}
6 changes: 6 additions & 0 deletions pkg/telemetry/workflowsbase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package telemetry

import (
"context"
"os"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -15,7 +16,12 @@ func TestWorkflowState(t *testing.T) {

r, err := w.Execute(context.Background())
require.NoError(t, err)

hostname, err := os.Hostname()
require.NoError(t, err)

require.EqualValues(t, provider.Report{
"uptime": 0,
"hn": hostname,
}, r)
}
8 changes: 7 additions & 1 deletion pkg/telemetry/workflowsk8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ const (
//
// {
// "k8s_pods_count": 21,
// "k8s_services_count": 3
// "k8s_services_count": 3,
// "k8s_gateways_count": 1
// }
func NewClusterStateWorkflow(d dynamic.Interface) (Workflow, error) {
if d == nil {
Expand All @@ -79,10 +80,15 @@ func NewClusterStateWorkflow(d dynamic.Interface) (Workflow, error) {
if err != nil {
return nil, err
}
providerGatewayCount, err := provider.NewK8sGatewayCountProvider(string(provider.GatewayCountKey), d)
if err != nil {
return nil, err
}

w := NewWorkflow(ClusterStateWorkflowName)
w.AddProvider(providerPodCount)
w.AddProvider(providerServiceCount)
w.AddProvider(providerGatewayCount)

return w, nil
}
22 changes: 21 additions & 1 deletion pkg/telemetry/workflowsk8s_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
dyn_fake "k8s.io/client-go/dynamic/fake"
clientgo_fake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme"
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"

"github.com/kong/kubernetes-telemetry/pkg/provider"
)
Expand Down Expand Up @@ -54,7 +56,16 @@ func TestWorkflowClusterState(t *testing.T) {
})

t.Run("properly reports cluster state", func(t *testing.T) {
dynClient := dyn_fake.NewSimpleDynamicClient(scheme.Scheme,
require.NoError(t, gatewayv1beta1.Install(scheme.Scheme))

dynClient := dyn_fake.NewSimpleDynamicClientWithCustomListKinds(scheme.Scheme,
map[schema.GroupVersionResource]string{
{
Group: "gateway.networking.k8s.io",
Version: "v1beta1",
Resource: "gateways",
}: "GatewayList",
},
&corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kong",
Expand All @@ -81,6 +92,12 @@ func TestWorkflowClusterState(t *testing.T) {
Name: "srv",
},
},
&gatewayv1beta1.Gateway{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kong",
Name: "gateway-1",
},
},
)

w, err := NewClusterStateWorkflow(dynClient)
Expand All @@ -93,6 +110,9 @@ func TestWorkflowClusterState(t *testing.T) {
require.EqualValues(t, provider.Report{
provider.PodCountKey: 1,
provider.ServiceCountKey: 2,
// TODO fix below count: it should be 1 but for some reason even after adding the GVR
// to scheme gateways can't be found by listing.
provider.GatewayCountKey: 0,
}, r)
})
}

0 comments on commit fe6d3b8

Please sign in to comment.