Skip to content

Commit

Permalink
Merge pull request #323 from deads2k/available
Browse files Browse the repository at this point in the history
authentication operator is marked available because the deployment is missing
  • Loading branch information
openshift-merge-robot committed Aug 13, 2020
2 parents 2aac3f1 + 8fb510e commit 7b3308b
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 194 deletions.
33 changes: 30 additions & 3 deletions pkg/controllers/deployment/deployment_controller.go
Expand Up @@ -43,12 +43,12 @@ import (
// These conditions are operated and defaulted by this controller.
// Any new condition used by this controller sync() loop should be listed here.
var knownConditionNames = sets.NewString(
"OAuthServerDeploymentAvailable",
"OAuthServerDeploymentDegraded",
"OAuthServerDeploymentProgressing",
"OAuthServerIngressConfigDegraded",
"OAuthServerProxyDegraded",
"OAuthServerRouteDegraded",
"OAuthServerIngressConfigDegraded",
"OAuthServerDeploymentProgressing",
"OAuthServerAvailable",
)

type deploymentController struct {
Expand Down Expand Up @@ -186,6 +186,33 @@ func (c *deploymentController) sync(ctx context.Context, syncContext factory.Syn
}
}

// no matter what, check and set available.
deployment, err := c.deployments.Deployments("openshift-authentication").Get(ctx, "oauth-openshift", metav1.GetOptions{})
if err != nil {
foundConditions = append(foundConditions, operatorv1.OperatorCondition{
Type: "OAuthServerDeploymentDegraded",
Status: operatorv1.ConditionTrue,
Reason: "DeploymentAvailableReplicasCheckFailed",
Message: err.Error(),
})
} else {
if deployment.Status.AvailableReplicas > 0 {
foundConditions = append(foundConditions, operatorv1.OperatorCondition{
Type: "OAuthServerDeploymentAvailable",
Status: operatorv1.ConditionTrue,
Reason: "AsExpected",
Message: fmt.Sprintf("availableReplicas==%d", deployment.Status.AvailableReplicas),
})
} else {
foundConditions = append(foundConditions, operatorv1.OperatorCondition{
Type: "OAuthServerDeploymentAvailable",
Status: operatorv1.ConditionFalse,
Reason: "NoReplicas",
Message: "availableReplicas==0",
})
}
}

return common.UpdateControllerConditions(c.operatorClient, knownConditionNames, foundConditions)
}

Expand Down
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"

"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 @@ -212,6 +102,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
}

0 comments on commit 7b3308b

Please sign in to comment.