mirror of
https://github.com/TwiN/gatus.git
synced 2024-11-21 23:43:27 +01:00
Start working on tests for auto discovery
This commit is contained in:
parent
dbc893fbc4
commit
d4d4ca236a
@ -19,7 +19,6 @@ kubernetes:
|
|||||||
cluster-mode: "out"
|
cluster-mode: "out"
|
||||||
auto-discover: true
|
auto-discover: true
|
||||||
excluded-service-suffixes:
|
excluded-service-suffixes:
|
||||||
- primary
|
|
||||||
- canary
|
- canary
|
||||||
service-template:
|
service-template:
|
||||||
interval: 30s
|
interval: 30s
|
||||||
|
@ -2,9 +2,13 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TwinProduction/gatus/core"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/TwinProduction/gatus/core"
|
||||||
|
"github.com/TwinProduction/gatus/k8stest"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetBeforeConfigIsLoaded(t *testing.T) {
|
func TestGetBeforeConfigIsLoaded(t *testing.T) {
|
||||||
@ -311,3 +315,84 @@ services:
|
|||||||
t.Errorf("config.Security.Basic.PasswordSha512Hash should've been %s, but was %s", expectedPasswordHash, config.Security.Basic.PasswordSha512Hash)
|
t.Errorf("config.Security.Basic.PasswordSha512Hash should've been %s, but was %s", expectedPasswordHash, config.Security.Basic.PasswordSha512Hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseAndValidateConfigBytesWithNoServicesOrAutoDiscovery(t *testing.T) {
|
||||||
|
_, err := parseAndValidateConfigBytes([]byte(``))
|
||||||
|
if err != ErrNoServiceInConfig {
|
||||||
|
t.Error("The error returned should have been of type ErrNoServiceInConfig")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseAndValidateConfigBytesWithKubernetesAutoDiscovery(t *testing.T) {
|
||||||
|
var kubernetesServices []v1.Service
|
||||||
|
kubernetesServices = append(kubernetesServices, k8stest.CreateTestServices("service-1", "default"))
|
||||||
|
kubernetesServices = append(kubernetesServices, k8stest.CreateTestServices("service-2", "default"))
|
||||||
|
kubernetesServices = append(kubernetesServices, k8stest.CreateTestServices("service-2-canary", "default"))
|
||||||
|
kubernetesServices = append(kubernetesServices, k8stest.CreateTestServices("service-3", "kube-system"))
|
||||||
|
kubernetesServices = append(kubernetesServices, k8stest.CreateTestServices("service-4", "tools"))
|
||||||
|
kubernetesServices = append(kubernetesServices, k8stest.CreateTestServices("service-5", "tools"))
|
||||||
|
kubernetesServices = append(kubernetesServices, k8stest.CreateTestServices("service-6", "tools"))
|
||||||
|
kubernetesServices = append(kubernetesServices, k8stest.CreateTestServices("service-7", "metrics"))
|
||||||
|
kubernetesServices = append(kubernetesServices, k8stest.CreateTestServices("service-7-canary", "metrics"))
|
||||||
|
k8stest.InitializeMockedKubernetesClient(kubernetesServices)
|
||||||
|
config, err := parseAndValidateConfigBytes([]byte(`
|
||||||
|
debug: true
|
||||||
|
|
||||||
|
kubernetes:
|
||||||
|
cluster-mode: "mock"
|
||||||
|
auto-discover: true
|
||||||
|
excluded-service-suffixes:
|
||||||
|
- canary
|
||||||
|
service-template:
|
||||||
|
interval: 29s
|
||||||
|
conditions:
|
||||||
|
- "[STATUS] == 200"
|
||||||
|
namespaces:
|
||||||
|
- name: default
|
||||||
|
hostname-suffix: ".default.svc.cluster.local"
|
||||||
|
target-path: "/health"
|
||||||
|
- name: tools
|
||||||
|
hostname-suffix: ".tools.svc.cluster.local"
|
||||||
|
target-path: "/health"
|
||||||
|
excluded-services:
|
||||||
|
- service-6
|
||||||
|
- name: metrics
|
||||||
|
hostname-suffix: ".metrics.svc.cluster.local"
|
||||||
|
target-path: "/health"
|
||||||
|
`))
|
||||||
|
if err != nil {
|
||||||
|
t.Error("No error should've been returned")
|
||||||
|
}
|
||||||
|
if config == nil {
|
||||||
|
t.Fatal("Config shouldn't have been nil")
|
||||||
|
}
|
||||||
|
if config.Kubernetes == nil {
|
||||||
|
t.Fatal("Kuberbetes config shouldn't have been nil")
|
||||||
|
}
|
||||||
|
if len(config.Services) != 5 {
|
||||||
|
t.Error("Expected 5 services to have been added through k8s auto discovery, got", len(config.Services))
|
||||||
|
}
|
||||||
|
for _, service := range config.Services {
|
||||||
|
if service.Name == "service-2-canary" || service.Name == "service-7-canary" {
|
||||||
|
t.Errorf("service '%s' should've been excluded because excluded-service-suffixes has 'canary'", service.Name)
|
||||||
|
} else if service.Name == "service-6" {
|
||||||
|
t.Errorf("service '%s' should've been excluded because excluded-services has 'service-6'", service.Name)
|
||||||
|
} else if service.Name == "service-3" {
|
||||||
|
t.Errorf("service '%s' should've been excluded because the namespace 'kube-system' is not configured for auto discovery", service.Name)
|
||||||
|
} else {
|
||||||
|
if service.Interval != 29*time.Second {
|
||||||
|
t.Errorf("service '%s' should've had an interval of 29s, because the template is configured for it", service.Name)
|
||||||
|
}
|
||||||
|
if len(service.Conditions) != 1 {
|
||||||
|
t.Errorf("service '%s' should've had 1 condition", service.Name)
|
||||||
|
}
|
||||||
|
if len(service.Conditions) == 1 && *service.Conditions[0] != "[STATUS] == 200" {
|
||||||
|
t.Errorf("service '%s' should've had the condition '[STATUS] == 200', because the template is configured for it", service.Name)
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(service.URL, ".svc.cluster.local/health") {
|
||||||
|
t.Errorf("service '%s' should've had an URL with the suffix '.svc.cluster.local/health'", service.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -6,28 +6,64 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/TwinProduction/gatus/k8stest"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// KubernetesClientApi is a minimal interface for interacting with Kubernetes
|
||||||
|
// Created mostly to make mocking the Kubernetes client easier
|
||||||
|
type KubernetesClientApi interface {
|
||||||
|
GetServices(namespace string) ([]v1.Service, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesClient is a working implementation of KubernetesClientApi
|
||||||
|
type KubernetesClient struct {
|
||||||
|
client *kubernetes.Clientset
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServices returns a list of services for a given namespace
|
||||||
|
func (k *KubernetesClient) GetServices(namespace string) ([]v1.Service, error) {
|
||||||
|
services, err := k.client.CoreV1().Services(namespace).List(metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return services.Items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKubernetesClient creates a KubernetesClient
|
||||||
|
func NewKubernetesClient(client *kubernetes.Clientset) *KubernetesClient {
|
||||||
|
return &KubernetesClient{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewClient creates a Kubernetes client for the given ClusterMode
|
// NewClient creates a Kubernetes client for the given ClusterMode
|
||||||
func NewClient(clusterMode ClusterMode) (*kubernetes.Clientset, error) {
|
func NewClient(clusterMode ClusterMode) (KubernetesClientApi, error) {
|
||||||
var kubeConfig *rest.Config
|
var kubeConfig *rest.Config
|
||||||
var err error
|
var err error
|
||||||
switch clusterMode {
|
switch clusterMode {
|
||||||
case ClusterModeIn:
|
case ClusterModeIn:
|
||||||
kubeConfig, err = getInClusterConfig()
|
kubeConfig, err = rest.InClusterConfig()
|
||||||
case ClusterModeOut:
|
case ClusterModeOut:
|
||||||
kubeConfig, err = getOutClusterConfig()
|
kubeConfig, err = getOutClusterConfig()
|
||||||
|
case ClusterModeMock:
|
||||||
|
return k8stest.GetMockedKubernetesClient(), nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid cluster mode, try '%s' or '%s'", ClusterModeIn, ClusterModeOut)
|
return nil, fmt.Errorf("invalid cluster mode, try '%s' or '%s'", ClusterModeIn, ClusterModeOut)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to get cluster config for mode '%s': %s", clusterMode, err.Error())
|
return nil, fmt.Errorf("unable to get cluster config for mode '%s': %s", clusterMode, err.Error())
|
||||||
}
|
}
|
||||||
return kubernetes.NewForConfig(kubeConfig)
|
client, err := kubernetes.NewForConfig(kubeConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewKubernetesClient(client), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func homeDir() string {
|
func homeDir() string {
|
||||||
@ -47,7 +83,3 @@ func getOutClusterConfig() (*rest.Config, error) {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
return clientcmd.BuildConfigFromFlags("", *kubeConfig)
|
return clientcmd.BuildConfigFromFlags("", *kubeConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getInClusterConfig() (*rest.Config, error) {
|
|
||||||
return rest.InClusterConfig()
|
|
||||||
}
|
|
||||||
|
@ -41,4 +41,5 @@ type ClusterMode string
|
|||||||
const (
|
const (
|
||||||
ClusterModeIn ClusterMode = "in"
|
ClusterModeIn ClusterMode = "in"
|
||||||
ClusterModeOut ClusterMode = "out"
|
ClusterModeOut ClusterMode = "out"
|
||||||
|
ClusterModeMock ClusterMode = "mock"
|
||||||
)
|
)
|
||||||
|
14
k8s/k8s.go
14
k8s/k8s.go
@ -1,16 +1,10 @@
|
|||||||
package k8s
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
corev1 "k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetKubernetesServices return List of Services from given namespace
|
// GetKubernetesServices return a list of Services from the given namespace
|
||||||
func GetKubernetesServices(client *kubernetes.Clientset, ns string) ([]corev1.Service, error) {
|
func GetKubernetesServices(client KubernetesClientApi, namespace string) ([]v1.Service, error) {
|
||||||
services, err := client.CoreV1().Services(ns).List(metav1.ListOptions{})
|
return client.GetServices(namespace)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return services.Items, nil
|
|
||||||
}
|
}
|
||||||
|
53
k8stest/k8stest.go
Normal file
53
k8stest/k8stest.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package k8stest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mockedKubernetesClient *MockKubernetesClient
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockKubernetesClient is a mocked implementation of k8s.KubernetesClientApi
|
||||||
|
type MockKubernetesClient struct {
|
||||||
|
Services []v1.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServices returns a list of services in a given namespace
|
||||||
|
func (mock *MockKubernetesClient) GetServices(namespace string) ([]v1.Service, error) {
|
||||||
|
var services []v1.Service
|
||||||
|
for _, service := range mock.Services {
|
||||||
|
if service.Namespace == namespace {
|
||||||
|
services = append(services, service)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return services, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMockedKubernetesClient returns a mocked implementation of k8s.KubernetesClientApi
|
||||||
|
func GetMockedKubernetesClient() *MockKubernetesClient {
|
||||||
|
if mockedKubernetesClient != nil {
|
||||||
|
return mockedKubernetesClient
|
||||||
|
}
|
||||||
|
InitializeMockedKubernetesClient(nil)
|
||||||
|
return mockedKubernetesClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitializeMockedKubernetesClient initializes a MockKubernetesClient with a given list of services
|
||||||
|
func InitializeMockedKubernetesClient(services []v1.Service) {
|
||||||
|
mockedKubernetesClient = &MockKubernetesClient{
|
||||||
|
Services: services,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateTestServices creates a mocked service for testing purposes
|
||||||
|
func CreateTestServices(name, namespace string) v1.Service {
|
||||||
|
return v1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Namespace: namespace,
|
||||||
|
},
|
||||||
|
Spec: v1.ServiceSpec{},
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,13 @@ package metric
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/TwinProduction/gatus/config"
|
"github.com/TwinProduction/gatus/config"
|
||||||
"github.com/TwinProduction/gatus/core"
|
"github.com/TwinProduction/gatus/core"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
Loading…
Reference in New Issue
Block a user