Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

authentication operator is marked available because the deployment is missing #323

Merged
merged 4 commits into from Aug 13, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
148 changes: 30 additions & 118 deletions pkg/controllers/endpointaccessible/endpoint_accessible_controller.go
Expand Up @@ -4,134 +4,23 @@ import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"strconv"
"sync"
"time"

routev1informers "github.com/openshift/client-go/route/informers/externalversions/route/v1"
routev1listers "github.com/openshift/client-go/route/listers/route/v1"
operatorv1 "github.com/openshift/api/operator/v1"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no need for this empty line

"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/events"
"github.com/openshift/library-go/pkg/operator/v1helpers"

utilerrors "k8s.io/apimachinery/pkg/util/errors"
corev1informers "k8s.io/client-go/informers/core/v1"
corev1listers "k8s.io/client-go/listers/core/v1"
)

// NewOAuthRouteCheckController returns a controller that checks the health of authentication route.
func NewOAuthRouteCheckController(
operatorClient v1helpers.OperatorClient,
routeInformerNamespaces routev1informers.RouteInformer,
recorder events.Recorder,
) factory.Controller {
routeLister := routeInformerNamespaces.Lister()
routeInformer := routeInformerNamespaces.Informer()
endpointListFunc := func() ([]string, error) {
return listOAuthRoutes(routeLister, recorder)
}

return NewEndpointAccessibleController(
"OAuthRouteCheck",
operatorClient, endpointListFunc, []factory.Informer{routeInformer}, recorder)
}

// NewOAuthServiceCheckController returns a controller that checks the health of authentication service.
func NewOAuthServiceCheckController(
operatorClient v1helpers.OperatorClient,
corev1Informers corev1informers.Interface,
recorder events.Recorder,
) factory.Controller {
serviceLister := corev1Informers.Services().Lister()
serviceInformer := corev1Informers.Services().Informer()
endpointsListFunc := func() ([]string, error) {
return listOAuthServices(serviceLister, recorder)
}

return NewEndpointAccessibleController(
"OAuthServiceCheck",
operatorClient, endpointsListFunc, []factory.Informer{serviceInformer}, recorder)
}

// NewOAuthServiceEndpointsCheckController returns a controller that checks the health of authentication service
// endpoints.
func NewOAuthServiceEndpointsCheckController(
operatorClient v1helpers.OperatorClient,
corev1Informers corev1informers.Interface,
recorder events.Recorder,
) factory.Controller {
endpointsLister := corev1Informers.Endpoints().Lister()
endpointsInformer := corev1Informers.Endpoints().Informer()

endpointsListFn := func() ([]string, error) {
return listOAuthServiceEndpoints(endpointsLister, recorder)
}

return NewEndpointAccessibleController(
"OAuthServiceEndpointsCheck",
operatorClient, endpointsListFn, []factory.Informer{endpointsInformer}, recorder)
}

func listOAuthServiceEndpoints(endpointsLister corev1listers.EndpointsLister, recorder events.Recorder) ([]string, error) {
var results []string
endpoints, err := endpointsLister.Endpoints("openshift-authentication").Get("oauth-openshift")
if err != nil {
recorder.Warningf("OAuthServiceEndpointsCheck", "failed to get oauth service endpoints: %v", err)
return results, nil
}
for _, subset := range endpoints.Subsets {
for _, address := range subset.Addresses {
for _, port := range subset.Ports {
results = append(results, net.JoinHostPort(address.IP, strconv.Itoa(int(port.Port))))
}
}
}
if len(results) == 0 {
return nil, fmt.Errorf("oauth service endpoints are not ready")
}
return toHealthzURL(results), nil
}

func listOAuthServices(serviceLister corev1listers.ServiceLister, recorder events.Recorder) ([]string, error) {
var results []string
service, err := serviceLister.Services("openshift-authentication").Get("oauth-openshift")
if err != nil {
recorder.Warningf("OAuthServiceCheck", "failed to get oauth service: %v", err)
return nil, err
}
for _, port := range service.Spec.Ports {
results = append(results, net.JoinHostPort(service.Spec.ClusterIP, strconv.Itoa(int(port.Port))))
}
if len(results) == 0 {
return nil, fmt.Errorf("no valid oauth services found")
}
return toHealthzURL(results), nil
}

func listOAuthRoutes(routeLister routev1listers.RouteLister, recorder events.Recorder) ([]string, error) {
var results []string
route, err := routeLister.Routes("openshift-authentication").Get("oauth-openshift")
if err != nil {
recorder.Warningf("OAuthRouteCheck", "failed to get oauth route: %v", err)
return nil, err
}
for _, ingress := range route.Status.Ingress {
if len(ingress.Host) > 0 {
results = append(results, ingress.Host)
}
}
if len(results) == 0 {
recorder.Warningf("OAuthRouteCheck", "route status does not have host address")
return nil, fmt.Errorf("route status does not have host address")
}
return toHealthzURL(results), nil
}

type endpointAccessibleController struct {
operatorClient v1helpers.OperatorClient
endpointListFn EndpointListFunc
operatorClient v1helpers.OperatorClient
endpointListFn EndpointListFunc
availableConditionName string
}

type EndpointListFunc func() ([]string, error)
Expand All @@ -146,8 +35,9 @@ func NewEndpointAccessibleController(
recorder events.Recorder,
) factory.Controller {
c := &endpointAccessibleController{
operatorClient: operatorClient,
endpointListFn: endpointListFn,
operatorClient: operatorClient,
endpointListFn: endpointListFn,
availableConditionName: name + "EndpointAccessibleControllerAvailable",
}

return factory.New().
Expand Down Expand Up @@ -207,6 +97,28 @@ func (c *endpointAccessibleController) sync(ctx context.Context, syncCtx factory
errors = append(errors, err)
}

// if at least one endpoint responded, we are available
if len(errors) < len(endpoints) {
if _, _, err := v1helpers.UpdateStatus(c.operatorClient, v1helpers.UpdateConditionFn(operatorv1.OperatorCondition{
Type: c.availableConditionName,
Status: operatorv1.ConditionTrue,
Reason: "AsExpected",
})); err != nil {
// append the error to be degraded
errors = append(errors, err)
}
} else {
if _, _, err := v1helpers.UpdateStatus(c.operatorClient, v1helpers.UpdateConditionFn(operatorv1.OperatorCondition{
Type: c.availableConditionName,
Status: operatorv1.ConditionFalse,
Reason: "EndpointUnavailable",
Message: utilerrors.NewAggregate(errors).Error(),
})); err != nil {
// append the error to be degraded
errors = append(errors, err)
}
}

return utilerrors.NewAggregate(errors)
}

Expand Down
Expand Up @@ -6,6 +6,9 @@ import (
"reflect"
"testing"

operatorv1 "github.com/openshift/api/operator/v1"
"github.com/openshift/library-go/pkg/operator/v1helpers"

"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/events"
)
Expand Down Expand Up @@ -47,6 +50,7 @@ func Test_endpointAccessibleController_sync(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &endpointAccessibleController{
operatorClient: v1helpers.NewFakeOperatorClient(&operatorv1.OperatorSpec{}, &operatorv1.OperatorStatus{}, nil),
endpointListFn: tt.endpointListFn,
}
if err := c.sync(context.Background(), factory.NewSyncContext(tt.name, events.NewInMemoryRecorder(tt.name))); (err != nil) != tt.wantErr {
Expand Down
124 changes: 124 additions & 0 deletions pkg/controllers/endpointaccessible/endpoints_accessible.go
@@ -0,0 +1,124 @@
package endpointaccessible

import (
"fmt"
"net"
"strconv"

routev1informers "github.com/openshift/client-go/route/informers/externalversions/route/v1"
routev1listers "github.com/openshift/client-go/route/listers/route/v1"
"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/events"
"github.com/openshift/library-go/pkg/operator/v1helpers"

corev1informers "k8s.io/client-go/informers/core/v1"
corev1listers "k8s.io/client-go/listers/core/v1"
)

// NewOAuthRouteCheckController returns a controller that checks the health of authentication route.
func NewOAuthRouteCheckController(
operatorClient v1helpers.OperatorClient,
routeInformerNamespaces routev1informers.RouteInformer,
recorder events.Recorder,
) factory.Controller {
routeLister := routeInformerNamespaces.Lister()
routeInformer := routeInformerNamespaces.Informer()
endpointListFunc := func() ([]string, error) {
return listOAuthRoutes(routeLister, recorder)
}

return NewEndpointAccessibleController(
"OAuthRouteCheck",
operatorClient, endpointListFunc, []factory.Informer{routeInformer}, recorder)
}

// NewOAuthServiceCheckController returns a controller that checks the health of authentication service.
func NewOAuthServiceCheckController(
operatorClient v1helpers.OperatorClient,
corev1Informers corev1informers.Interface,
recorder events.Recorder,
) factory.Controller {
serviceLister := corev1Informers.Services().Lister()
serviceInformer := corev1Informers.Services().Informer()
endpointsListFunc := func() ([]string, error) {
return listOAuthServices(serviceLister, recorder)
}

return NewEndpointAccessibleController(
"OAuthServiceCheck",
operatorClient, endpointsListFunc, []factory.Informer{serviceInformer}, recorder)
}

// NewOAuthServiceEndpointsCheckController returns a controller that checks the health of authentication service
// endpoints.
func NewOAuthServiceEndpointsCheckController(
operatorClient v1helpers.OperatorClient,
corev1Informers corev1informers.Interface,
recorder events.Recorder,
) factory.Controller {
endpointsLister := corev1Informers.Endpoints().Lister()
endpointsInformer := corev1Informers.Endpoints().Informer()

endpointsListFn := func() ([]string, error) {
return listOAuthServiceEndpoints(endpointsLister, recorder)
}

return NewEndpointAccessibleController(
"OAuthServiceEndpointsCheck",
operatorClient, endpointsListFn, []factory.Informer{endpointsInformer}, recorder)
}

func listOAuthServiceEndpoints(endpointsLister corev1listers.EndpointsLister, recorder events.Recorder) ([]string, error) {
var results []string
endpoints, err := endpointsLister.Endpoints("openshift-authentication").Get("oauth-openshift")
if err != nil {
recorder.Warningf("OAuthServiceEndpointsCheck", "failed to get oauth service endpoints: %v", err)
return results, nil
}
for _, subset := range endpoints.Subsets {
for _, address := range subset.Addresses {
for _, port := range subset.Ports {
results = append(results, net.JoinHostPort(address.IP, strconv.Itoa(int(port.Port))))
}
}
}
if len(results) == 0 {
return nil, fmt.Errorf("oauth service endpoints are not ready")
}
return toHealthzURL(results), nil
}

func listOAuthServices(serviceLister corev1listers.ServiceLister, recorder events.Recorder) ([]string, error) {
var results []string
service, err := serviceLister.Services("openshift-authentication").Get("oauth-openshift")
if err != nil {
recorder.Warningf("OAuthServiceCheck", "failed to get oauth service: %v", err)
return nil, err
}
for _, port := range service.Spec.Ports {
results = append(results, net.JoinHostPort(service.Spec.ClusterIP, strconv.Itoa(int(port.Port))))
}
if len(results) == 0 {
return nil, fmt.Errorf("no valid oauth services found")
}
return toHealthzURL(results), nil
}

func listOAuthRoutes(routeLister routev1listers.RouteLister, recorder events.Recorder) ([]string, error) {
var results []string
route, err := routeLister.Routes("openshift-authentication").Get("oauth-openshift")
if err != nil {
recorder.Warningf("OAuthRouteCheck", "failed to get oauth route: %v", err)
return nil, err
}
for _, ingress := range route.Status.Ingress {
if len(ingress.Host) > 0 {
results = append(results, ingress.Host)
}
}
if len(results) == 0 {
recorder.Warningf("OAuthRouteCheck", "route status does not have host address")
return nil, fmt.Errorf("route status does not have host address")
}
return toHealthzURL(results), nil
}