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  }