package kubernetes import ( "context" "errors" "net/url" "syscall" "testing" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/client/interceptor" "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider" ) type listFunc func(ctx context.Context, c client.WithWatch, list client.ObjectList, opts ...client.ListOption) error func newCheckProvider(list listFunc) *Provider { scheme := runtime.NewScheme() if err := corev1.AddToScheme(scheme); err != nil { panic(err) } c := fake.NewClientBuilder().WithScheme(scheme).WithInterceptorFuncs(interceptor.Funcs{List: list}).Build() p := newWithClient(c, provider.ProviderConfig{Name: "kubernetes"}) p.apiHost = "https://10.96.0.1:443" return p } func TestCheck_reachable(t *testing.T) { t.Parallel() p := newTestProvider(testPod("ns", "a", "uid-a", corev1.PodRunning, "10.0.0.1")) p.apiHost = "https://10.96.0.1:443" got := p.Check(context.Background()) if len(got) != 1 { t.Fatalf("Check() returned %d targets, want 1", len(got)) } ts := got[0] if !ts.Ready || ts.Reason != provider.ReasonReachable || ts.Message != "" { t.Errorf("Check() = %+v, want ready and Reachable", ts) } if ts.Name != "apiserver" || ts.Address != "https://10.96.0.1:443" { t.Errorf("target = %q at %q, want apiserver at https://10.96.0.1:443", ts.Name, ts.Address) } } func TestCheck_listOptions(t *testing.T) { t.Parallel() var got client.ListOptions p := newCheckProvider(func(ctx context.Context, c client.WithWatch, list client.ObjectList, opts ...client.ListOption) error { got.ApplyOptions(opts) return c.List(ctx, list, opts...) }) p.Check(context.Background()) if got.Limit != 1 { t.Errorf("Limit = %d, want 1", got.Limit) } if got.Namespace != "" { t.Errorf("Namespace = %q, want cluster-wide", got.Namespace) } want := provider.LabelManaged + "=" + provider.LabelManagedYes if got.LabelSelector == nil || got.LabelSelector.String() != want { t.Errorf("LabelSelector = %v, want %s", got.LabelSelector, want) } } func TestCheck_failureReasons(t *testing.T) { t.Parallel() pods := schema.GroupResource{Resource: "pods"} ended, cancel := context.WithCancel(context.Background()) cancel() tests := []struct { name string ctx context.Context err error want string }{ {"forbidden", context.Background(), apierrors.NewForbidden(pods, "", errors.New("rbac denied")), provider.ReasonForbidden}, {"unauthorized", context.Background(), apierrors.NewUnauthorized("token expired"), provider.ReasonUnauthorized}, {"api timeout", context.Background(), apierrors.NewTimeoutError("slow etcd", 1), provider.ReasonTimeout}, {"server timeout", context.Background(), apierrors.NewServerTimeout(pods, "list", 1), provider.ReasonTimeout}, {"context ended", ended, context.Canceled, provider.ReasonTimeout}, {"internal error", context.Background(), apierrors.NewInternalError(errors.New("etcd down")), provider.ReasonAPIError}, {"connection refused", context.Background(), &url.Error{ Op: "Get", URL: "https://10.96.0.1:443/api/v1/pods", Err: syscall.ECONNREFUSED, }, provider.ReasonUnreachable}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { t.Parallel() p := newCheckProvider(func(context.Context, client.WithWatch, client.ObjectList, ...client.ListOption) error { return tc.err }) got := p.Check(tc.ctx) if len(got) != 1 { t.Fatalf("Check() returned %d targets, want 1", len(got)) } if got[0].Ready || got[0].Reason != tc.want || got[0].Message != tc.err.Error() { t.Errorf("Check() = %+v, want not ready, reason %s, message %q", got[0], tc.want, tc.err.Error()) } }) } }