github.com/verrazzano/verrazzano@v1.7.0/pkg/k8s/ready/available.go (about) 1 // Copyright (c) 2022, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package ready 5 6 import ( 7 "context" 8 "fmt" 9 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 10 vzapi "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1" 11 appsv1 "k8s.io/api/apps/v1" 12 apierrors "k8s.io/apimachinery/pkg/api/errors" 13 "k8s.io/apimachinery/pkg/types" 14 clipkg "sigs.k8s.io/controller-runtime/pkg/client" 15 ) 16 17 const ( 18 typeDeployment = "deployment" 19 typeStatefulset = "statefulset" 20 typeDaemonset = "daemonset" 21 ) 22 23 type ( 24 isObjectAvailableSig func(client clipkg.Client, nsn types.NamespacedName) error 25 AvailabilityObjects struct { 26 StatefulsetNames []types.NamespacedName 27 DeploymentNames []types.NamespacedName 28 DeploymentSelectors []clipkg.ListOption 29 DaemonsetNames []types.NamespacedName 30 } 31 ) 32 33 func (c *AvailabilityObjects) IsAvailable(log vzlog.VerrazzanoLogger, client clipkg.Client) (string, vzapi.ComponentAvailability) { 34 if err := DeploymentsAreAvailable(client, c.DeploymentNames); err != nil { 35 return handleNotAvailableError(log, err) 36 } 37 if err := DeploymentsAreAvailableBySelector(client, c.DeploymentSelectors); err != nil { 38 return handleNotAvailableError(log, err) 39 } 40 if err := StatefulSetsAreAvailable(client, c.StatefulsetNames); err != nil { 41 return handleNotAvailableError(log, err) 42 } 43 if err := DaemonsetsAreAvailable(client, c.DaemonsetNames); err != nil { 44 return handleNotAvailableError(log, err) 45 } 46 return "", vzapi.ComponentAvailable 47 } 48 49 func handleNotAvailableError(log vzlog.VerrazzanoLogger, err error) (string, vzapi.ComponentAvailability) { 50 log.Progressf(err.Error()) 51 return err.Error(), vzapi.ComponentUnavailable 52 } 53 54 // DeploymentsAreAvailable a list of deployments is available when the expected replicas is equal to the ready replicas 55 func DeploymentsAreAvailable(client clipkg.Client, deployments []types.NamespacedName) error { 56 return objectsAreAvailable(client, deployments, isDeploymentAvailable) 57 } 58 59 // StatefulSetsAreAvailable a list of statefulsets is available when the expected replicas is equal to the ready replicas 60 func StatefulSetsAreAvailable(client clipkg.Client, statefulsets []types.NamespacedName) error { 61 return objectsAreAvailable(client, statefulsets, isStatefulsetAvailable) 62 } 63 64 // DaemonsetsAreAvailable a list of daemonsets is available when the expected replicas is equal to the ready replicas 65 func DaemonsetsAreAvailable(client clipkg.Client, daemonsets []types.NamespacedName) error { 66 return objectsAreAvailable(client, daemonsets, isDaemonsetAvailable) 67 } 68 69 func DeploymentsAreAvailableBySelector(client clipkg.Client, selectors []clipkg.ListOption) error { 70 if len(selectors) < 1 { 71 return nil 72 } 73 deploymentList := &appsv1.DeploymentList{} 74 if err := client.List(context.TODO(), deploymentList, selectors...); err != nil { 75 return handleListFailure(typeDeployment, err) 76 } 77 if deploymentList.Items == nil || len(deploymentList.Items) < 1 { 78 return handleListNotFound(typeDeployment, selectors) 79 } 80 for _, deploy := range deploymentList.Items { 81 nsn := types.NamespacedName{ 82 Namespace: deploy.Namespace, 83 Name: deploy.Name, 84 } 85 if err := handleReplicasNotReady(deploy.Status.ReadyReplicas, deploy.Status.Replicas, nsn, typeDeployment); err != nil { 86 return err 87 } 88 } 89 return nil 90 } 91 92 // StatefulSetsAreAvailableBySelector returns an error if not all pods controlled by statefulsets selected by the selector are ready 93 func StatefulSetsAreAvailableBySelector(client clipkg.Client, selectors []clipkg.ListOption) error { 94 if len(selectors) < 1 { 95 return nil 96 } 97 statefulsetList := &appsv1.StatefulSetList{} 98 if err := client.List(context.TODO(), statefulsetList, selectors...); err != nil { 99 return handleListFailure(typeStatefulset, err) 100 } 101 if statefulsetList.Items == nil || len(statefulsetList.Items) < 1 { 102 return handleListNotFound(typeStatefulset, selectors) 103 } 104 for _, sts := range statefulsetList.Items { 105 nsn := types.NamespacedName{ 106 Namespace: sts.Namespace, 107 Name: sts.Name, 108 } 109 if err := handleReplicasNotReady(sts.Status.ReadyReplicas, sts.Status.Replicas, nsn, typeStatefulset); err != nil { 110 return err 111 } 112 } 113 return nil 114 } 115 116 func objectsAreAvailable(client clipkg.Client, objectKeys []types.NamespacedName, objectAvailableFunc isObjectAvailableSig) error { 117 for _, nsn := range objectKeys { 118 if err := objectAvailableFunc(client, nsn); err != nil { 119 return err 120 } 121 } 122 return nil 123 } 124 125 func isDeploymentAvailable(client clipkg.Client, nsn types.NamespacedName) error { 126 deploy := &appsv1.Deployment{} 127 if err := client.Get(context.TODO(), nsn, deploy); err != nil { 128 return handleGetError(err, nsn, typeDeployment) 129 } 130 return handleReplicasNotReady(deploy.Status.ReadyReplicas, deploy.Status.Replicas, nsn, typeDeployment) 131 } 132 133 func isStatefulsetAvailable(client clipkg.Client, nsn types.NamespacedName) error { 134 sts := &appsv1.StatefulSet{} 135 if err := client.Get(context.TODO(), nsn, sts); err != nil { 136 return handleGetError(err, nsn, typeStatefulset) 137 } 138 return handleReplicasNotReady(sts.Status.ReadyReplicas, sts.Status.Replicas, nsn, typeStatefulset) 139 } 140 141 func isDaemonsetAvailable(client clipkg.Client, nsn types.NamespacedName) error { 142 ds := &appsv1.DaemonSet{} 143 if err := client.Get(context.TODO(), nsn, ds); err != nil { 144 return handleGetError(err, nsn, typeDaemonset) 145 } 146 return handleReplicasNotReady(ds.Status.NumberReady, ds.Status.DesiredNumberScheduled, nsn, typeDaemonset) 147 } 148 149 func handleGetError(err error, nsn types.NamespacedName, objectType string) error { 150 if apierrors.IsNotFound(err) { 151 return fmt.Errorf("waiting for %s %v to exist", objectType, nsn) 152 } 153 return fmt.Errorf("failed getting %s %v: %v", objectType, nsn, err) 154 } 155 156 func handleReplicasNotReady(ready, expected int32, nsn types.NamespacedName, objectType string) error { 157 if expected == 0 { 158 // an enabled component should have at least one expected replica 159 expected = 1 160 } 161 if ready != expected { 162 return fmt.Errorf("%s %v not available: %d/%d replicas ready", objectType, nsn, ready, expected) 163 } 164 return nil 165 } 166 167 func handleListFailure(objectType string, err error) error { 168 return fmt.Errorf("failed getting %s: %v", objectType, err) 169 } 170 171 func handleListNotFound(objectType string, selectors []clipkg.ListOption) error { 172 return fmt.Errorf("waiting for %s matching selectors %v to exist", objectType, selectors) 173 }