github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/pkg/clients/integration/snapshots.go (about)

     1  package integration
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/devfile/library/v2/pkg/util"
     9  	. "github.com/onsi/ginkgo/v2"
    10  	appstudioApi "github.com/redhat-appstudio/application-api/api/v1alpha1"
    11  	"github.com/redhat-appstudio/e2e-tests/pkg/constants"
    12  	"github.com/redhat-appstudio/e2e-tests/pkg/logs"
    13  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    14  	intgteststat "github.com/konflux-ci/integration-service/pkg/integrationteststatus"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/apimachinery/pkg/types"
    17  	"k8s.io/apimachinery/pkg/util/wait"
    18  	"sigs.k8s.io/controller-runtime/pkg/client"
    19  )
    20  
    21  // SnapshotTestsStatusAnnotation is annotation in snapshot where integration test results are stored
    22  const SnapshotTestsStatusAnnotation = "test.appstudio.openshift.io/status"
    23  
    24  // CreateSnapshotWithComponents creates a Snapshot using the given parameters.
    25  func (i *IntegrationController) CreateSnapshotWithComponents(snapshotName, componentName, applicationName, namespace string, snapshotComponents []appstudioApi.SnapshotComponent) (*appstudioApi.Snapshot, error) {
    26  	snapshot := &appstudioApi.Snapshot{
    27  		ObjectMeta: metav1.ObjectMeta{
    28  			Name:      snapshotName,
    29  			Namespace: namespace,
    30  			Labels: map[string]string{
    31  				"test.appstudio.openshift.io/type":           "component",
    32  				"appstudio.openshift.io/component":           componentName,
    33  				"pac.test.appstudio.openshift.io/event-type": "push",
    34  			},
    35  		},
    36  		Spec: appstudioApi.SnapshotSpec{
    37  			Application: applicationName,
    38  			Components:  snapshotComponents,
    39  		},
    40  	}
    41  	return snapshot, i.KubeRest().Create(context.Background(), snapshot)
    42  }
    43  
    44  // CreateSnapshotWithImage creates a snapshot using an image.
    45  func (i *IntegrationController) CreateSnapshotWithImage(componentName, applicationName, namespace, containerImage string) (*appstudioApi.Snapshot, error) {
    46  	snapshotComponents := []appstudioApi.SnapshotComponent{
    47  		{
    48  			Name:           componentName,
    49  			ContainerImage: containerImage,
    50  		},
    51  	}
    52  
    53  	snapshotName := "snapshot-sample-" + util.GenerateRandomString(4)
    54  
    55  	return i.CreateSnapshotWithComponents(snapshotName, componentName, applicationName, namespace, snapshotComponents)
    56  }
    57  
    58  // GetSnapshotByComponent returns the first snapshot in namespace if exist, else will return nil
    59  func (i *IntegrationController) GetSnapshotByComponent(namespace string) (*appstudioApi.Snapshot, error) {
    60  	snapshot := &appstudioApi.SnapshotList{}
    61  	opts := []client.ListOption{
    62  		client.MatchingLabels{
    63  			"test.appstudio.openshift.io/type": "component",
    64  		},
    65  		client.InNamespace(namespace),
    66  	}
    67  	err := i.KubeRest().List(context.Background(), snapshot, opts...)
    68  
    69  	if err == nil && len(snapshot.Items) > 0 {
    70  		return &snapshot.Items[0], nil
    71  	}
    72  	return nil, err
    73  }
    74  
    75  // GetSnapshot returns the Snapshot in the namespace and nil if it's not found
    76  // It will search for the Snapshot based on the Snapshot name, associated PipelineRun name or Component name
    77  // In the case the List operation fails, an error will be returned.
    78  func (i *IntegrationController) GetSnapshot(snapshotName, pipelineRunName, componentName, namespace string) (*appstudioApi.Snapshot, error) {
    79  	ctx := context.Background()
    80  	// If Snapshot name is provided, try to get the resource directly
    81  	if len(snapshotName) > 0 {
    82  		snapshot := &appstudioApi.Snapshot{}
    83  		if err := i.KubeRest().Get(ctx, types.NamespacedName{Name: snapshotName, Namespace: namespace}, snapshot); err != nil {
    84  			return nil, fmt.Errorf("couldn't find Snapshot with name '%s' in '%s' namespace", snapshotName, namespace)
    85  		}
    86  		return snapshot, nil
    87  	}
    88  	// Search for the Snapshot in the namespace based on the associated Component or PipelineRun
    89  	snapshots := &appstudioApi.SnapshotList{}
    90  	opts := []client.ListOption{
    91  		client.InNamespace(namespace),
    92  	}
    93  	err := i.KubeRest().List(ctx, snapshots, opts...)
    94  	if err != nil {
    95  		return nil, fmt.Errorf("error when listing Snapshots in '%s' namespace", namespace)
    96  	}
    97  	for _, snapshot := range snapshots.Items {
    98  		if snapshot.Name == snapshotName {
    99  			return &snapshot, nil
   100  		}
   101  		// find snapshot by pipelinerun name
   102  		if len(pipelineRunName) > 0 && snapshot.Labels["appstudio.openshift.io/build-pipelinerun"] == pipelineRunName {
   103  			return &snapshot, nil
   104  
   105  		}
   106  		// find snapshot by component name
   107  		if len(componentName) > 0 && snapshot.Labels["appstudio.openshift.io/component"] == componentName {
   108  			return &snapshot, nil
   109  
   110  		}
   111  	}
   112  	return nil, fmt.Errorf("no snapshot found for component '%s', pipelineRun '%s' in '%s' namespace", componentName, pipelineRunName, namespace)
   113  }
   114  
   115  // DeleteSnapshot removes given snapshot from specified namespace.
   116  func (i *IntegrationController) DeleteSnapshot(hasSnapshot *appstudioApi.Snapshot, namespace string) error {
   117  	err := i.KubeRest().Delete(context.Background(), hasSnapshot)
   118  	return err
   119  }
   120  
   121  // PatchSnapshot patches the given snapshot with the provided patch.
   122  func (i *IntegrationController) PatchSnapshot(oldSnapshot *appstudioApi.Snapshot, newSnapshot *appstudioApi.Snapshot) error {
   123  	patch := client.MergeFrom(oldSnapshot)
   124  	err := i.KubeRest().Patch(context.Background(), newSnapshot, patch)
   125  	return err
   126  }
   127  
   128  // DeleteAllSnapshotsInASpecificNamespace removes all snapshots from a specific namespace. Useful when creating a lot of resources and want to remove all of them
   129  func (i *IntegrationController) DeleteAllSnapshotsInASpecificNamespace(namespace string, timeout time.Duration) error {
   130  	if err := i.KubeRest().DeleteAllOf(context.Background(), &appstudioApi.Snapshot{}, client.InNamespace(namespace)); err != nil {
   131  		return fmt.Errorf("error deleting snapshots from the namespace %s: %+v", namespace, err)
   132  	}
   133  
   134  	return utils.WaitUntil(func() (done bool, err error) {
   135  		snapshotList, err := i.ListAllSnapshots(namespace)
   136  		if err != nil {
   137  			return false, nil
   138  		}
   139  		return len(snapshotList.Items) == 0, nil
   140  	}, timeout)
   141  }
   142  
   143  // WaitForSnapshotToGetCreated wait for the Snapshot to get created successfully.
   144  func (i *IntegrationController) WaitForSnapshotToGetCreated(snapshotName, pipelinerunName, componentName, testNamespace string) (*appstudioApi.Snapshot, error) {
   145  	var snapshot *appstudioApi.Snapshot
   146  
   147  	err := wait.PollUntilContextTimeout(context.Background(), constants.PipelineRunPollingInterval, 10*time.Minute, true, func(ctx context.Context) (done bool, err error) {
   148  		snapshot, err = i.GetSnapshot(snapshotName, pipelinerunName, componentName, testNamespace)
   149  		if err != nil {
   150  			GinkgoWriter.Printf("unable to get the Snapshot within the namespace %s. Error: %v", testNamespace, err)
   151  			return false, nil
   152  		}
   153  
   154  		return true, nil
   155  	})
   156  
   157  	return snapshot, err
   158  }
   159  
   160  // ListAllSnapshots returns a list of all Snapshots in a given namespace.
   161  func (i *IntegrationController) ListAllSnapshots(namespace string) (*appstudioApi.SnapshotList, error) {
   162  	snapshotList := &appstudioApi.SnapshotList{}
   163  	err := i.KubeRest().List(context.Background(), snapshotList, &client.ListOptions{Namespace: namespace})
   164  
   165  	return snapshotList, err
   166  }
   167  
   168  // StoreSnapshot stores a given Snapshot as an artifact.
   169  func (i *IntegrationController) StoreSnapshot(snapshot *appstudioApi.Snapshot) error {
   170  	return logs.StoreResourceYaml(snapshot, "snapshot-"+snapshot.Name)
   171  }
   172  
   173  // StoreAllSnapshots stores all Snapshots in a given namespace.
   174  func (i *IntegrationController) StoreAllSnapshots(namespace string) error {
   175  	snapshotList, err := i.ListAllSnapshots(namespace)
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	for _, snapshot := range snapshotList.Items {
   181  		if err := i.StoreSnapshot(&snapshot); err != nil {
   182  			return err
   183  		}
   184  	}
   185  	return nil
   186  }
   187  
   188  // GetIntegrationTestStatusDetailFromSnapshot parses snapshot annotation and returns integration test status detail
   189  func (i *IntegrationController) GetIntegrationTestStatusDetailFromSnapshot(snapshot *appstudioApi.Snapshot, scenarioName string) (*intgteststat.IntegrationTestStatusDetail, error) {
   190  	var (
   191  		resultsJson string
   192  		ok          bool
   193  	)
   194  	annotations := snapshot.GetAnnotations()
   195  	resultsJson, ok = annotations[SnapshotTestsStatusAnnotation]
   196  	if !ok {
   197  		resultsJson = ""
   198  	}
   199  	statuses, err := intgteststat.NewSnapshotIntegrationTestStatuses(resultsJson)
   200  	if err != nil {
   201  		return nil, fmt.Errorf("failed to create new SnapshotIntegrationTestStatuses object: %w", err)
   202  	}
   203  	statusDetail, ok := statuses.GetScenarioStatus(scenarioName)
   204  	if !ok {
   205  		return nil, fmt.Errorf("status detail for scenario %s not found", scenarioName)
   206  	}
   207  	return statusDetail, nil
   208  }