github.com/redhat-appstudio/e2e-tests@v0.0.0-20230619105049-9a422b2094d7/pkg/utils/release/controller.go (about) 1 package release 2 3 import ( 4 "context" 5 "crypto/tls" 6 "fmt" 7 "io" 8 "net/http" 9 "time" 10 11 appstudioApi "github.com/redhat-appstudio/application-api/api/v1alpha1" 12 kubeCl "github.com/redhat-appstudio/e2e-tests/pkg/apis/kubernetes" 13 "github.com/redhat-appstudio/e2e-tests/pkg/utils" 14 releaseApi "github.com/redhat-appstudio/release-service/api/v1alpha1" 15 releaseMetadata "github.com/redhat-appstudio/release-service/metadata" 16 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" 17 corev1 "k8s.io/api/core/v1" 18 k8sErrors "k8s.io/apimachinery/pkg/api/errors" 19 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 "k8s.io/apimachinery/pkg/types" 21 "k8s.io/utils/pointer" 22 "sigs.k8s.io/controller-runtime/pkg/client" 23 ) 24 25 type SuiteController struct { 26 *kubeCl.CustomClient 27 } 28 29 func NewSuiteController(kube *kubeCl.CustomClient) (*SuiteController, error) { 30 return &SuiteController{ 31 kube, 32 }, nil 33 } 34 35 type StrategyConfig struct { 36 Mapping Mapping `json:"mapping"` 37 } 38 type Mapping struct { 39 Components []Component `json:"components"` 40 } 41 type Component struct { 42 Name string `json:"name"` 43 Repository string `json:"repository"` 44 } 45 46 func (s *SuiteController) GenerateReleaseStrategyConfig(components []Component) *StrategyConfig { 47 return &StrategyConfig{ 48 Mapping{Components: components}, 49 } 50 } 51 52 // CreateSnapshot creates a Snapshot using the given parameters. 53 func (s *SuiteController) CreateSnapshot(name string, namespace string, applicationName string, snapshotComponents []appstudioApi.SnapshotComponent) (*appstudioApi.Snapshot, error) { 54 snapshot := &appstudioApi.Snapshot{ 55 ObjectMeta: metav1.ObjectMeta{ 56 Name: name, 57 Namespace: namespace, 58 }, 59 Spec: appstudioApi.SnapshotSpec{ 60 Application: applicationName, 61 Components: snapshotComponents, 62 }, 63 } 64 return snapshot, s.KubeRest().Create(context.TODO(), snapshot) 65 } 66 67 // GetSnapshotByComponent returns the first snapshot in namespace if exist, else will return nil 68 func (s *SuiteController) GetSnapshotByComponent(namespace string) (*appstudioApi.Snapshot, error) { 69 snapshot := &appstudioApi.SnapshotList{} 70 opts := []client.ListOption{ 71 client.MatchingLabels{ 72 "test.appstudio.openshift.io/type": "component", 73 }, 74 client.InNamespace(namespace), 75 } 76 err := s.KubeRest().List(context.TODO(), snapshot, opts...) 77 78 if err == nil && len(snapshot.Items) > 0 { 79 return &snapshot.Items[0], nil 80 } 81 return nil, err 82 } 83 84 // CreateRelease creates a new Release using the given parameters. 85 func (s *SuiteController) CreateRelease(name, namespace, snapshot, releasePlan string) (*releaseApi.Release, error) { 86 release := &releaseApi.Release{ 87 ObjectMeta: metav1.ObjectMeta{ 88 Name: name, 89 Namespace: namespace, 90 }, 91 Spec: releaseApi.ReleaseSpec{ 92 Snapshot: snapshot, 93 ReleasePlan: releasePlan, 94 }, 95 } 96 97 return release, s.KubeRest().Create(context.TODO(), release) 98 } 99 100 // CreateReleaseStrategy creates a new ReleaseStrategy using the given parameters. 101 func (s *SuiteController) CreateReleaseStrategy(name, namespace, pipelineName, bundle string, policy string, serviceAccount string, params []releaseApi.Params) (*releaseApi.ReleaseStrategy, error) { 102 releaseStrategy := &releaseApi.ReleaseStrategy{ 103 ObjectMeta: metav1.ObjectMeta{ 104 Name: name, 105 Namespace: namespace, 106 }, 107 Spec: releaseApi.ReleaseStrategySpec{ 108 Pipeline: pipelineName, 109 Bundle: bundle, 110 Policy: policy, 111 Params: params, 112 ServiceAccount: serviceAccount, 113 }, 114 } 115 116 return releaseStrategy, s.KubeRest().Create(context.TODO(), releaseStrategy) 117 } 118 119 // GetPipelineRunInNamespace returns the Release PipelineRun referencing the given release. 120 func (s *SuiteController) GetPipelineRunInNamespace(namespace, releaseName, releaseNamespace string) (*v1beta1.PipelineRun, error) { 121 pipelineRuns := &v1beta1.PipelineRunList{} 122 opts := []client.ListOption{ 123 client.MatchingLabels{ 124 "release.appstudio.openshift.io/name": releaseName, 125 "release.appstudio.openshift.io/namespace": releaseNamespace, 126 }, 127 client.InNamespace(namespace), 128 } 129 130 err := s.KubeRest().List(context.TODO(), pipelineRuns, opts...) 131 132 if err == nil && len(pipelineRuns.Items) > 0 { 133 return &pipelineRuns.Items[0], nil 134 } 135 136 return nil, fmt.Errorf("couldn't find PipelineRun in managed namespace '%s' for a release '%s' in '%s' namespace", namespace, releaseName, releaseNamespace) 137 } 138 139 // GetRelease returns the release with in the given namespace. 140 // It can find a Release CR based on provided name or a name of an associated Snapshot 141 func (s *SuiteController) GetRelease(releaseName, snapshotName, namespace string) (*releaseApi.Release, error) { 142 ctx := context.Background() 143 if len(releaseName) > 0 { 144 release := &releaseApi.Release{} 145 err := s.KubeRest().Get(ctx, types.NamespacedName{Name: releaseName, Namespace: namespace}, release) 146 if err != nil { 147 return nil, fmt.Errorf("failed to get Release with name '%s' in '%s' namespace", releaseName, namespace) 148 } 149 return release, nil 150 } 151 releaseList := &releaseApi.ReleaseList{} 152 opts := []client.ListOption{ 153 client.InNamespace(namespace), 154 } 155 if err := s.KubeRest().List(context.TODO(), releaseList, opts...); err != nil { 156 return nil, err 157 } 158 for _, r := range releaseList.Items { 159 if len(snapshotName) > 0 && r.Spec.Snapshot == snapshotName { 160 return &r, nil 161 } 162 } 163 return nil, fmt.Errorf("could not find Release CR based on associated Snapshot '%s' in '%s' namespace", snapshotName, namespace) 164 } 165 166 // GetRelease returns the list of Release CR in the given namespace. 167 func (s *SuiteController) GetReleases(namespace string) (*releaseApi.ReleaseList, error) { 168 releaseList := &releaseApi.ReleaseList{} 169 opts := []client.ListOption{ 170 client.InNamespace(namespace), 171 } 172 err := s.KubeRest().List(context.TODO(), releaseList, opts...) 173 174 return releaseList, err 175 } 176 177 // GetFirstReleaseInNamespace returns the first Release from list of releases in the given namespace. 178 func (s *SuiteController) GetFirstReleaseInNamespace(namespace string) (*releaseApi.Release, error) { 179 releaseList := &releaseApi.ReleaseList{} 180 opts := []client.ListOption{ 181 client.InNamespace(namespace), 182 } 183 184 err := s.KubeRest().List(context.TODO(), releaseList, opts...) 185 if err != nil || len(releaseList.Items) < 1 { 186 return nil, fmt.Errorf("could not find any Releases in namespace %s: %+v", namespace, err) 187 } 188 return &releaseList.Items[0], nil 189 } 190 191 // GetReleasePlanAdmission returns the ReleasePlanAdmission with the given name in the given namespace. 192 func (s *SuiteController) GetReleasePlanAdmission(name, namespace string) (*releaseApi.ReleasePlanAdmission, error) { 193 releasePlanAdmission := &releaseApi.ReleasePlanAdmission{} 194 195 err := s.KubeRest().Get(context.TODO(), types.NamespacedName{ 196 Name: name, 197 Namespace: namespace, 198 }, releasePlanAdmission) 199 200 return releasePlanAdmission, err 201 } 202 203 // DeleteReleasePlanAdmission deletes the ReleasePlanAdmission resource with the given name from the given namespace. 204 // Optionally, it can avoid returning an error if the resource did not exist: 205 // specify 'false', if it's likely the ReleasePlanAdmission has already been deleted (for example, because the Namespace was deleted) 206 func (s *SuiteController) DeleteReleasePlanAdmission(name, namespace string, failOnNotFound bool) error { 207 releasePlanAdmission := releaseApi.ReleasePlanAdmission{ 208 ObjectMeta: metav1.ObjectMeta{ 209 Name: name, 210 Namespace: namespace, 211 }, 212 } 213 err := s.KubeRest().Delete(context.TODO(), &releasePlanAdmission) 214 if err != nil && !failOnNotFound && k8sErrors.IsNotFound(err) { 215 err = nil 216 } 217 return err 218 } 219 220 // CreateReleasePlan creates a new ReleasePlan using the given parameters. 221 func (s *SuiteController) CreateReleasePlan(name, namespace, application, targetNamespace, autoReleaseLabel string) (*releaseApi.ReleasePlan, error) { 222 var releasePlan *releaseApi.ReleasePlan = &releaseApi.ReleasePlan{ 223 ObjectMeta: metav1.ObjectMeta{ 224 GenerateName: name, 225 Name: name, 226 Namespace: namespace, 227 Labels: map[string]string{ 228 releaseMetadata.AutoReleaseLabel: autoReleaseLabel, 229 releaseMetadata.AttributionLabel: "true", 230 }, 231 }, 232 Spec: releaseApi.ReleasePlanSpec{ 233 DisplayName: name, 234 Application: application, 235 Target: targetNamespace, 236 }, 237 } 238 if autoReleaseLabel == "" || autoReleaseLabel == "true" { 239 releasePlan.ObjectMeta.Labels[releaseMetadata.AutoReleaseLabel] = "true" 240 } else { 241 releasePlan.ObjectMeta.Labels[releaseMetadata.AutoReleaseLabel] = "false" 242 } 243 244 return releasePlan, s.KubeRest().Create(context.TODO(), releasePlan) 245 } 246 247 // GetReleasePlan returns the ReleasePlan with the given name in the given namespace. 248 func (s *SuiteController) GetReleasePlan(name, namespace string) (*releaseApi.ReleasePlan, error) { 249 releasePlan := &releaseApi.ReleasePlan{} 250 251 err := s.KubeRest().Get(context.TODO(), types.NamespacedName{ 252 Name: name, 253 Namespace: namespace, 254 }, releasePlan) 255 256 return releasePlan, err 257 } 258 259 // DeleteReleasePlan deletes a given ReleasePlan name in given namespace. 260 func (s *SuiteController) DeleteReleasePlan(name, namespace string, failOnNotFound bool) error { 261 releasePlan := &releaseApi.ReleasePlan{ 262 ObjectMeta: metav1.ObjectMeta{ 263 Name: name, 264 Namespace: namespace, 265 }, 266 } 267 err := s.KubeRest().Delete(context.TODO(), releasePlan) 268 if err != nil && !failOnNotFound && k8sErrors.IsNotFound(err) { 269 err = nil 270 } 271 return err 272 } 273 274 // CreateReleasePlanAdmission creates a new ReleasePlanAdmission using the given parameters. 275 func (s *SuiteController) CreateReleasePlanAdmission(name, originNamespace, application, namespace, environment, autoRelease, releaseStrategy string) (*releaseApi.ReleasePlanAdmission, error) { 276 var releasePlanAdmission *releaseApi.ReleasePlanAdmission = &releaseApi.ReleasePlanAdmission{ 277 ObjectMeta: metav1.ObjectMeta{ 278 Name: name, 279 Namespace: namespace, 280 }, 281 Spec: releaseApi.ReleasePlanAdmissionSpec{ 282 DisplayName: name, 283 Application: application, 284 Origin: originNamespace, 285 Environment: environment, 286 ReleaseStrategy: releaseStrategy, 287 }, 288 } 289 if autoRelease != "" { 290 releasePlanAdmission.ObjectMeta.Labels[releaseMetadata.AutoReleaseLabel] = autoRelease 291 } 292 return releasePlanAdmission, s.KubeRest().Create(context.TODO(), releasePlanAdmission) 293 } 294 295 // CreateRegistryJsonSecret creates a secret for registry repository in namespace given with key passed. 296 func (s *SuiteController) CreateRegistryJsonSecret(name, namespace, authKey, keyName string) (*corev1.Secret, error) { 297 secret := &corev1.Secret{ 298 ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, 299 Type: corev1.SecretTypeDockerConfigJson, 300 Data: map[string][]byte{".dockerconfigjson": []byte(fmt.Sprintf("{\"auths\":{\"quay.io\":{\"username\":\"%s\",\"password\":\"%s\",\"auth\":\"dGVzdDp0ZXN0\",\"email\":\"\"}}}", keyName, authKey))}, 301 } 302 err := s.KubeRest().Create(context.TODO(), secret) 303 if err != nil { 304 return nil, err 305 } 306 return secret, nil 307 } 308 309 // DeleteAllSnapshotsInASpecificNamespace removes all snapshots from a specific namespace. Useful when creating a lot of resources and want to remove all of them 310 func (s *SuiteController) DeleteAllSnapshotsInASpecificNamespace(namespace string, timeout time.Duration) error { 311 if err := s.KubeRest().DeleteAllOf(context.TODO(), &appstudioApi.Snapshot{}, client.InNamespace(namespace)); err != nil { 312 return fmt.Errorf("error deleting snapshots from the namespace %s: %+v", namespace, err) 313 } 314 315 snapshotList := &appstudioApi.SnapshotList{} 316 return utils.WaitUntil(func() (done bool, err error) { 317 if err := s.KubeRest().List(context.Background(), snapshotList, &client.ListOptions{Namespace: namespace}); err != nil { 318 return false, nil 319 } 320 return len(snapshotList.Items) == 0, nil 321 }, timeout) 322 } 323 324 // CreateComponentWithDockerSource creates a component based on container image source. 325 func (s *SuiteController) CreateComponentWithDockerSource(applicationName, componentName, namespace, gitSourceURL, containerImageSource, outputContainerImage, secret string) (*appstudioApi.Component, error) { 326 component := &appstudioApi.Component{ 327 ObjectMeta: metav1.ObjectMeta{ 328 Name: componentName, 329 Namespace: namespace, 330 }, 331 Spec: appstudioApi.ComponentSpec{ 332 ComponentName: componentName, 333 Application: applicationName, 334 Source: appstudioApi.ComponentSource{ 335 ComponentSourceUnion: appstudioApi.ComponentSourceUnion{ 336 GitSource: &appstudioApi.GitSource{ 337 URL: gitSourceURL, 338 DockerfileURL: containerImageSource, 339 }, 340 }, 341 }, 342 Secret: secret, 343 ContainerImage: outputContainerImage, 344 Replicas: pointer.Int(1), 345 TargetPort: 8081, 346 Route: "", 347 }, 348 } 349 err := s.KubeRest().Create(context.TODO(), component) 350 if err != nil { 351 return nil, err 352 } 353 return component, nil 354 } 355 356 // CreateComponentWithDockerSource creates a component based on container image source. 357 func (s *SuiteController) GetSbomPyxisByImageID(pyxisStageURL, imageID string, 358 pyxisCertDecoded, pyxisKeyDecoded []byte) ([]byte, error) { 359 360 url := fmt.Sprintf("%s%s", pyxisStageURL, imageID) 361 362 // Create a TLS configuration with the key and certificate 363 cert, err := tls.X509KeyPair(pyxisCertDecoded, pyxisKeyDecoded) 364 if err != nil { 365 return nil, fmt.Errorf("error creating TLS certificate and key: %s", err) 366 } 367 368 // Create a client with the custom TLS configuration 369 client := &http.Client{ 370 Transport: &http.Transport{ 371 TLSClientConfig: &tls.Config{ 372 Certificates: []tls.Certificate{cert}, 373 }, 374 }, 375 } 376 377 // Send GET request 378 request, err := http.NewRequest("GET", url, nil) 379 if err != nil { 380 return nil, fmt.Errorf("error creating GET request: %s", err) 381 } 382 383 response, err := client.Do(request) 384 if err != nil { 385 return nil, fmt.Errorf("error sending GET request: %s", err) 386 } 387 388 defer response.Body.Close() 389 390 // Read the response body 391 body, err := io.ReadAll(response.Body) 392 if err != nil { 393 return nil, fmt.Errorf("error reading response body: %s", err) 394 } 395 return body, nil 396 }