github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/install/apiservice.go (about) 1 package install 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 8 log "github.com/sirupsen/logrus" 9 apierrors "k8s.io/apimachinery/pkg/api/errors" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/apimachinery/pkg/labels" 12 apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" 13 14 "github.com/operator-framework/api/pkg/operators/v1alpha1" 15 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister" 16 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" 17 ) 18 19 func (i *StrategyDeploymentInstaller) createOrUpdateAPIService(caPEM []byte, desc v1alpha1.APIServiceDescription) error { 20 apiServiceName := fmt.Sprintf("%s.%s", desc.Version, desc.Group) 21 logger := log.WithFields(log.Fields{ 22 "owner": i.owner.GetName(), 23 "namespace": i.owner.GetNamespace(), 24 "apiservice": fmt.Sprintf("%s.%s", desc.Version, desc.Group), 25 }) 26 27 exists := true 28 apiService, err := i.strategyClient.GetOpLister().APIRegistrationV1().APIServiceLister().Get(apiServiceName) 29 if err != nil { 30 if !apierrors.IsNotFound(err) { 31 return err 32 } 33 34 exists = false 35 apiService = &apiregistrationv1.APIService{ 36 Spec: apiregistrationv1.APIServiceSpec{ 37 Group: desc.Group, 38 Version: desc.Version, 39 GroupPriorityMinimum: int32(2000), 40 VersionPriority: int32(15), 41 }, 42 } 43 apiService.SetName(apiServiceName) 44 45 ownerSubscription, err := i.findOwnerSubscription() 46 if err != nil { 47 return err 48 } else if ownerSubscription == nil { 49 // This is not an error. For example, the PackageServer CSV in OLM is created without a Subscription. 50 logger.Debugf("failed to get the owner subscription csv=%s", i.owner.GetName()) 51 } else if ownerSubscription.Spec.Config != nil { 52 apiService.SetAnnotations(ownerSubscription.Spec.Config.Annotations) 53 } 54 } else { 55 apiService = apiService.DeepCopy() 56 csv, ok := i.owner.(*v1alpha1.ClusterServiceVersion) 57 if !ok { 58 return fmt.Errorf("failed to typecast the APIService owner: APIServices require a CSV owner") 59 } 60 61 adoptable, err := IsAPIServiceAdoptable(i.strategyClient.GetOpLister(), csv, apiService) 62 if err != nil { 63 logger.WithFields(log.Fields{"obj": "apiService", "labels": apiService.GetLabels()}).Errorf("adoption check failed - %v", err) 64 } 65 66 if !adoptable { 67 return fmt.Errorf("pre-existing APIService %s.%s is not adoptable", desc.Version, desc.Group) 68 } 69 } 70 71 // Add the CSV as an owner 72 if err := ownerutil.AddOwnerLabels(apiService, i.owner); err != nil { 73 return err 74 } 75 apiService.Labels[OLMManagedLabelKey] = OLMManagedLabelValue 76 77 // Create a service for the deployment 78 containerPort := int32(443) 79 if desc.ContainerPort > 0 { 80 containerPort = desc.ContainerPort 81 } 82 // update the ServiceReference 83 apiService.Spec.Service = &apiregistrationv1.ServiceReference{ 84 Namespace: i.owner.GetNamespace(), 85 Name: ServiceName(desc.DeploymentName), 86 Port: &containerPort, 87 } 88 89 // create a fresh CA bundle 90 apiService.Spec.CABundle = caPEM 91 92 // attempt a update or create 93 if exists { 94 logger.Debug("updating APIService") 95 _, err = i.strategyClient.GetOpClient().UpdateAPIService(apiService) 96 } else { 97 logger.Debug("creating APIService") 98 _, err = i.strategyClient.GetOpClient().CreateAPIService(apiService) 99 } 100 101 if err != nil { 102 logger.Warnf("could not create or update APIService") 103 return err 104 } 105 106 return nil 107 } 108 109 func IsAPIServiceAdoptable(opLister operatorlister.OperatorLister, target *v1alpha1.ClusterServiceVersion, apiService *apiregistrationv1.APIService) (adoptable bool, err error) { 110 if apiService == nil || target == nil { 111 err = errors.New("invalid input") 112 return 113 } 114 115 apiServiceLabels := apiService.GetLabels() 116 ownerKind := apiServiceLabels[ownerutil.OwnerKind] 117 ownerName := apiServiceLabels[ownerutil.OwnerKey] 118 ownerNamespace := apiServiceLabels[ownerutil.OwnerNamespaceKey] 119 120 if ownerKind == "" || ownerNamespace == "" || ownerName == "" { 121 return 122 } 123 124 if err := ownerutil.InferGroupVersionKind(target); err != nil { 125 log.Warn(err.Error()) 126 } 127 128 targetKind := target.GetObjectKind().GroupVersionKind().Kind 129 if ownerKind != targetKind { 130 return 131 } 132 133 // Get the CSV that target replaces 134 replacing, replaceGetErr := opLister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(target.GetNamespace()).Get(target.Spec.Replaces) 135 if replaceGetErr != nil && !apierrors.IsNotFound(replaceGetErr) && !apierrors.IsGone(replaceGetErr) { 136 err = replaceGetErr 137 return 138 } 139 140 // Get the current owner CSV of the APIService 141 currentOwnerCSV, ownerGetErr := opLister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(ownerNamespace).Get(ownerName) 142 if ownerGetErr != nil && !apierrors.IsNotFound(ownerGetErr) && !apierrors.IsGone(ownerGetErr) { 143 err = ownerGetErr 144 return 145 } 146 147 owners := []ownerutil.Owner{target} 148 if replacing != nil { 149 owners = append(owners, replacing) 150 } 151 if currentOwnerCSV != nil && (currentOwnerCSV.Status.Phase == v1alpha1.CSVPhaseReplacing || currentOwnerCSV.Status.Phase == v1alpha1.CSVPhaseDeleting) { 152 owners = append(owners, currentOwnerCSV) 153 } 154 155 adoptable = ownerutil.AdoptableLabels(apiService.GetLabels(), true, owners...) 156 return 157 } 158 159 func IsAPIServiceAvailable(apiService *apiregistrationv1.APIService) bool { 160 for _, c := range apiService.Status.Conditions { 161 if c.Type == apiregistrationv1.Available && c.Status == apiregistrationv1.ConditionTrue { 162 return true 163 } 164 } 165 return false 166 } 167 168 // deleteLegacyAPIServiceResources deletes resources that were created by OLM for an APIService that used the old naming convention. 169 func (i *StrategyDeploymentInstaller) deleteLegacyAPIServiceResources(desc apiServiceDescriptionsWithCAPEM) error { 170 logger := log.WithFields(log.Fields{ 171 "ownerName": i.owner.GetName(), 172 "ownerNamespace": i.owner.GetNamespace(), 173 "ownerKind": i.owner.GetObjectKind().GroupVersionKind().GroupKind().Kind, 174 }) 175 namespace := i.owner.GetNamespace() 176 apiServiceName := fmt.Sprintf("%s.%s", desc.apiServiceDescription.Version, desc.apiServiceDescription.Group) 177 178 // Handle an edgecase where the legacy resources names matches the new names. 179 // This only occurs when the Group of the description matches the name of the deployment 180 // and the version is equal to "service". 181 if legacyAPIServiceNameToServiceName(apiServiceName) == ServiceName(desc.apiServiceDescription.DeploymentName) { 182 return nil 183 } 184 185 // Handle an edgecase where the legacy resources names matches the new names. 186 // This only occurs when the version of the description matches the name of the deployment 187 // and the group is equal to "service" 188 // If the names match, do not delete the service as OLM has already updated it. 189 legacyServiceName := legacyAPIServiceNameToServiceName(apiServiceName) 190 if legacyServiceName != ServiceName(desc.apiServiceDescription.DeploymentName) { 191 // Attempt to delete the legacy Service. 192 existingService, err := i.strategyClient.GetOpClient().GetService(namespace, legacyServiceName) 193 if err != nil { 194 if !apierrors.IsNotFound(err) { 195 return err 196 } 197 } else if ownerutil.AdoptableLabels(existingService.GetLabels(), true, i.owner) { 198 logger.Infof("Deleting Service with legacy APIService name %s", existingService.Name) 199 err = i.strategyClient.GetOpClient().DeleteService(namespace, legacyServiceName, &metav1.DeleteOptions{}) 200 if err != nil && !apierrors.IsNotFound(err) { 201 return err 202 } 203 } else { 204 logger.Infof("Service with legacy APIService resource name %s not adoptable", existingService.Name) 205 } 206 } else { 207 logger.Infof("New Service name matches legacy APIService resource name %s", legacyServiceName) 208 } 209 210 // Attempt to delete the legacy Secret. 211 existingSecret, err := i.strategyClient.GetOpClient().GetSecret(namespace, SecretName(apiServiceName)) 212 if err != nil { 213 if !apierrors.IsNotFound(err) { 214 return err 215 } 216 } else if ownerutil.AdoptableLabels(existingSecret.GetLabels(), true, i.owner) { 217 logger.Infof("Deleting Secret with legacy APIService name %s", existingSecret.Name) 218 err = i.strategyClient.GetOpClient().DeleteSecret(namespace, SecretName(apiServiceName), &metav1.DeleteOptions{}) 219 if err != nil && !apierrors.IsNotFound(err) { 220 return err 221 } 222 } else { 223 logger.Infof("Secret with legacy APIService %s not adoptable", existingSecret.Name) 224 } 225 226 // Attempt to delete the legacy Role. 227 existingRole, err := i.strategyClient.GetOpClient().GetRole(namespace, SecretName(apiServiceName)) 228 if err != nil { 229 if !apierrors.IsNotFound(err) { 230 return err 231 } 232 } else if ownerutil.AdoptableLabels(existingRole.GetLabels(), true, i.owner) { 233 logger.Infof("Deleting Role with legacy APIService name %s", existingRole.Name) 234 err = i.strategyClient.GetOpClient().DeleteRole(namespace, SecretName(apiServiceName), &metav1.DeleteOptions{}) 235 if err != nil && !apierrors.IsNotFound(err) { 236 return err 237 } 238 } else { 239 logger.Infof("Role with legacy APIService name %s not adoptable", existingRole.Name) 240 } 241 242 // Attempt to delete the legacy secret RoleBinding. 243 existingRoleBinding, err := i.strategyClient.GetOpClient().GetRoleBinding(namespace, SecretName(apiServiceName)) 244 if err != nil { 245 if !apierrors.IsNotFound(err) { 246 return err 247 } 248 } else if ownerutil.AdoptableLabels(existingRoleBinding.GetLabels(), true, i.owner) { 249 logger.Infof("Deleting RoleBinding with legacy APIService name %s", existingRoleBinding.Name) 250 err = i.strategyClient.GetOpClient().DeleteRoleBinding(namespace, SecretName(apiServiceName), &metav1.DeleteOptions{}) 251 if err != nil && !apierrors.IsNotFound(err) { 252 return err 253 } 254 } else { 255 logger.Infof("RoleBinding with legacy APIService name %s not adoptable", existingRoleBinding.Name) 256 } 257 258 // Attempt to delete the legacy ClusterRoleBinding. 259 existingClusterRoleBinding, err := i.strategyClient.GetOpClient().GetClusterRoleBinding(apiServiceName + "-system:auth-delegator") 260 if err != nil { 261 if !apierrors.IsNotFound(err) { 262 return err 263 } 264 } else if ownerutil.AdoptableLabels(existingClusterRoleBinding.GetLabels(), true, i.owner) { 265 logger.Infof("Deleting ClusterRoleBinding with legacy APIService name %s", existingClusterRoleBinding.Name) 266 err = i.strategyClient.GetOpClient().DeleteClusterRoleBinding(apiServiceName+"-system:auth-delegator", &metav1.DeleteOptions{}) 267 if err != nil && !apierrors.IsNotFound(err) { 268 return err 269 } 270 } else { 271 logger.Infof("ClusterRoleBinding with legacy APIService name %s not adoptable", existingClusterRoleBinding.Name) 272 } 273 274 // Attempt to delete the legacy AuthReadingRoleBinding. 275 existingRoleBinding, err = i.strategyClient.GetOpClient().GetRoleBinding(KubeSystem, apiServiceName+"-auth-reader") 276 if err != nil { 277 if !apierrors.IsNotFound(err) { 278 return err 279 } 280 } else if ownerutil.AdoptableLabels(existingRoleBinding.GetLabels(), true, i.owner) { 281 logger.Infof("Deleting RoleBinding with legacy APIService name %s", existingRoleBinding.Name) 282 err = i.strategyClient.GetOpClient().DeleteRoleBinding(KubeSystem, apiServiceName+"-auth-reader", &metav1.DeleteOptions{}) 283 if err != nil && !apierrors.IsNotFound(err) { 284 return err 285 } 286 } else { 287 logger.Infof("RoleBinding with legacy APIService name %s not adoptable", existingRoleBinding.Name) 288 } 289 290 return nil 291 } 292 293 // legacyAPIServiceNameToServiceName returns the result of replacing all 294 // periods in the given APIService name with hyphens 295 func legacyAPIServiceNameToServiceName(apiServiceName string) string { 296 // Replace all '.'s with "-"s to convert to a DNS-1035 label 297 return strings.Replace(apiServiceName, ".", "-", -1) 298 } 299 300 func (i *StrategyDeploymentInstaller) findOwnerSubscription() (*v1alpha1.Subscription, error) { 301 list, listErr := i.strategyClient.GetOpLister().OperatorsV1alpha1().SubscriptionLister().Subscriptions(i.owner.GetNamespace()).List(labels.Everything()) 302 if listErr != nil { 303 err := fmt.Errorf("failed to list subscription namespace=%s - %v", i.owner.GetNamespace(), listErr) 304 return nil, err 305 } 306 307 for idx := range list { 308 sub := list[idx] 309 if sub.Status.InstalledCSV == i.owner.GetName() { 310 return sub, nil 311 } 312 } 313 314 return nil, nil 315 }