...

Source file src/edge-infra.dev/pkg/edge/api/services/artifacts/integration/integration_test.go

Documentation: edge-infra.dev/pkg/edge/api/services/artifacts/integration

     1  package integration
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/google/uuid"
    10  	"github.com/stretchr/testify/assert"
    11  
    12  	"edge-infra.dev/pkg/edge/api/services"
    13  	"edge-infra.dev/pkg/edge/api/services/artifacts"
    14  	clustersvc "edge-infra.dev/pkg/edge/api/services/cluster"
    15  	sqlquery "edge-infra.dev/pkg/edge/api/sql"
    16  	"edge-infra.dev/pkg/edge/api/sql/plugin"
    17  	sqlutils "edge-infra.dev/pkg/edge/api/sql/utils"
    18  	"edge-infra.dev/pkg/edge/api/testutils/seededpostgres"
    19  	"edge-infra.dev/pkg/edge/api/types"
    20  	"edge-infra.dev/pkg/edge/constants/api/fleet"
    21  	"edge-infra.dev/pkg/lib/runtime/version"
    22  	"edge-infra.dev/test/f2"
    23  	"edge-infra.dev/test/f2/x/postgres"
    24  )
    25  
    26  var (
    27  	f f2.Framework
    28  )
    29  
    30  func TestMain(m *testing.M) {
    31  	f = f2.New(
    32  		context.Background(),
    33  		f2.WithExtensions(
    34  			postgres.New(postgres.ApplySeedModel()),
    35  		)).
    36  		Setup(func(ctx f2.Context) (f2.Context, error) {
    37  			// TODO: set up a single shared postgres server or
    38  			// defer setup to each feature test?
    39  			return ctx, nil
    40  		})
    41  	os.Exit(f.Run(m))
    42  }
    43  
    44  func TestArtifactsService_AutoUpdates(t *testing.T) {
    45  	latestVersionForTest := "0.16.0"
    46  	seedData := seededpostgres.Seed
    47  	// Extra seed data used to validate auto-update corner cases:
    48  	// - UPDATEs in the CTES dont add to the rows affected return value, used to count number of affected clusters
    49  	// - cluster fleet_version should only be updated for clusters with matching labels
    50  	clusterEdgeID := uuid.New().String()
    51  	seedData = append(seedData,
    52  		plugin.Seed{
    53  			Name:     "seed-clusters.tmpl",
    54  			Priority: 4,
    55  			Data: []sqlutils.Cluster{
    56  				{
    57  					EdgeID:       clusterEdgeID,
    58  					ProjectID:    "test-org",
    59  					Active:       true,
    60  					Registered:   true,
    61  					SiteID:       "test_bsl_site_id",
    62  					Name:         "test_auto_store_cluster",
    63  					BannerID:     "3396a52c-6a22-4049-9593-5a63b596a101",
    64  					FleetVersion: "1.2.3-test",
    65  				},
    66  			},
    67  		},
    68  		plugin.Seed{
    69  			Name:     "seed-cluster-labels.tmpl",
    70  			Priority: 6,
    71  			Data: []sqlutils.ClusterLabel{
    72  				{
    73  					ClusterEdgeID: clusterEdgeID,
    74  					// store label. label_type = 'edge-fleet' and label_key = 'store'. required
    75  					// for auto-update to find and set store:$version for this cluster
    76  					LabelEdgeID: "e0ad2eca-03af-4fd1-91b0-36f98ca0c2cc",
    77  				},
    78  			},
    79  		},
    80  		plugin.Seed{
    81  			Name:     "seed-cluster-artifact-versions.tmpl",
    82  			Priority: 21,
    83  			Data: []sqlutils.ClusterArtifactVersion{
    84  				{
    85  					ClusterEdgeID:   clusterEdgeID,
    86  					ArtifactName:    fleet.Store,
    87  					ArtifactVersion: "1.2.3-test",
    88  				},
    89  			},
    90  		},
    91  		plugin.Seed{
    92  			Name:     "seed-cluster-config.tmpl",
    93  			Priority: 21,
    94  			Data: []sqlutils.ClusterConfig{
    95  				{
    96  					ClusterConfigEdgeID: uuid.NewString(),
    97  					ClusterEdgeID:       clusterEdgeID,
    98  					ConfigKey:           "auto_update_enabled",
    99  					ConfigValue:         "true",
   100  				},
   101  			},
   102  		},
   103  	)
   104  	feat := f2.NewFeature("Artifact auto-updates").
   105  		Setup("Add Seed data", postgres.WithData(seedData)).
   106  		Test("Service automatically applies latest version to clusters with auto-update enabled", func(ctx f2.Context, t *testing.T) f2.Context {
   107  			pg := postgres.FromContextT(ctx, t)
   108  			db := pg.DB()
   109  			clusterLabelSvc := clustersvc.NewLabelService(db)
   110  			svc := artifacts.NewArtifactsService(db, clusterLabelSvc)
   111  			bsSvc := services.NewBootstrapService("", nil, db)
   112  			// 6ae03a3b is a seeded store with auto-update enabled
   113  			storeUUID := "6ae03a3b-654d-4140-ac50-69d8b93a94ea"
   114  
   115  			// Sanity check that the seeded data has the expected initial state
   116  			fleetType, err := clusterLabelSvc.FetchFleetType(ctx, storeUUID)
   117  			assert.NoError(t, err)
   118  			assert.Equal(t, fleet.Store, fleetType.String())
   119  			version, err := bsSvc.GetClusterFleetVersion(ctx, storeUUID)
   120  			assert.NoError(t, err)
   121  			// db trigger will update version to 0.15.0 upon insertion
   122  			assert.Equal(t, "0.15.0", version)
   123  			artifacts, err := svc.GetClusterArtifactVersions(ctx, storeUUID)
   124  			assert.NoError(t, err)
   125  			assert.Len(t, artifacts, 1)
   126  			assert.Equal(t, types.ArtifactVersion{
   127  				Name:    fleet.Store,
   128  				Version: "0.0.0-test",
   129  			}, artifacts[0])
   130  
   131  			// Auto-update store clusters to latest version. Expected n will need to be updated
   132  			// if new cluster seeds are added
   133  			assert.NoError(t, insertNewLatestArtifactVersion(db, fleet.Store, latestVersionForTest))
   134  			n, err := svc.UpdateClustersToLatestArtifactVersion(ctx, fleet.Store)
   135  			assert.NoError(t, err)
   136  			assert.Equal(t, 2, n)
   137  			versionUpdated, err := bsSvc.GetClusterFleetVersion(ctx, storeUUID)
   138  			assert.NoError(t, err)
   139  			assert.Equal(t, latestVersionForTest, versionUpdated)
   140  			artifactsUpdated, err := svc.GetClusterArtifactVersions(ctx, storeUUID)
   141  			assert.NoError(t, err)
   142  			assert.Equal(t, types.ArtifactVersion{
   143  				Name:    fleet.Store,
   144  				Version: latestVersionForTest,
   145  			}, artifactsUpdated[0])
   146  
   147  			return ctx
   148  		}).
   149  		Test("Auto-update only affects clusters with a fleet type matching the new artifact", func(ctx f2.Context, t *testing.T) f2.Context {
   150  			pg := postgres.FromContextT(ctx, t)
   151  			db := pg.DB()
   152  			svc := artifacts.NewArtifactsService(db, nil)
   153  			bsSvc := services.NewBootstrapService("", nil, db)
   154  
   155  			// 6ae03a3b-... is skipped because it is a store, and the artifact being updated is basic-store
   156  			n, err := svc.UpdateClustersToLatestArtifactVersion(ctx, fleet.BasicStore)
   157  			assert.NoError(t, err)
   158  			assert.Equal(t, 0, n)
   159  
   160  			// "test_auto_store_cluster" is not skipped because it was labeled as a store
   161  			nonStoreClusterVersion, err := bsSvc.GetClusterFleetVersion(ctx, clusterEdgeID)
   162  			assert.NoError(t, err)
   163  			assert.Equal(t, "0.16.0", nonStoreClusterVersion)
   164  			nonStoreClusterArtifactsUpdated, err := svc.GetClusterArtifactVersions(ctx, clusterEdgeID)
   165  			assert.NoError(t, err)
   166  			assert.Equal(t, types.ArtifactVersion{
   167  				Name:    fleet.Store,
   168  				Version: "0.16.0",
   169  			}, nonStoreClusterArtifactsUpdated[0])
   170  
   171  			return ctx
   172  		}).Feature()
   173  	f.Test(t, feat)
   174  }
   175  
   176  func TestArtifactsService(t *testing.T) {
   177  	seedData := seededpostgres.Seed
   178  	feat := f2.NewFeature("Artifacts service").
   179  		Setup("Add Seed data", postgres.WithData(seedData)).
   180  		Test("Service handles getting and updating cluster fleet versions", func(ctx f2.Context, t *testing.T) f2.Context {
   181  			pg := postgres.FromContextT(ctx, t)
   182  			db := pg.DB()
   183  			clusterLabelSvc := clustersvc.NewLabelService(db)
   184  			svc := artifacts.NewArtifactsService(db, clusterLabelSvc)
   185  			bsSvc := services.NewBootstrapService("", nil, db)
   186  			storeUUID := "dc8e59c3-6338-4c28-a776-f54e93a19ff4"
   187  
   188  			// Sanity check that the seeded store cluster exists
   189  			fleetType, err := clusterLabelSvc.FetchFleetType(ctx, storeUUID)
   190  			assert.NoError(t, err)
   191  			assert.Equal(t, fleet.Store, fleetType.String())
   192  			version, err := bsSvc.GetClusterFleetVersion(ctx, storeUUID)
   193  			assert.NoError(t, err)
   194  			assert.Equal(t, "0.14.0-seed", version)
   195  
   196  			// New stores should have a single artifact, store
   197  			artifacts, err := svc.GetClusterArtifactVersions(ctx, storeUUID)
   198  			assert.NoError(t, err)
   199  			assert.Len(t, artifacts, 1)
   200  			assert.Equal(t, types.ArtifactVersion{
   201  				Name:    fleet.Store,
   202  				Version: "0.14.0-seed",
   203  			}, artifacts[0])
   204  
   205  			// Updating "Edge version" of a store should move the fleet_version and store cluster_artifact_version
   206  			assert.NoError(t, svc.UpdateClusterFleetVersionAndArtifact(ctx,
   207  				storeUUID, "0.15"),
   208  			)
   209  			versionUpdated, err := bsSvc.GetClusterFleetVersion(ctx, storeUUID)
   210  			assert.NoError(t, err)
   211  			assert.Equal(t, "0.15", versionUpdated)
   212  			artifactsUpdated, err := svc.GetClusterArtifactVersions(ctx, storeUUID)
   213  			assert.NoError(t, err)
   214  			assert.Equal(t, types.ArtifactVersion{
   215  				Name:    fleet.Store,
   216  				Version: "0.15",
   217  			}, artifactsUpdated[0])
   218  
   219  			return ctx
   220  		}).Feature()
   221  	f.Test(t, feat)
   222  }
   223  
   224  func TestArtifactsService_Migrations(t *testing.T) {
   225  	feat := f2.NewFeature("Migration manager successfully runs artifact migrations").
   226  		Setup("Add Seed data", postgres.WithData(seededpostgres.Seed)).
   227  		Test("Latest tags are converted to static versions", func(ctx f2.Context, t *testing.T) f2.Context {
   228  			pg := postgres.FromContextT(ctx, t)
   229  			db := pg.DB()
   230  
   231  			row := db.QueryRowContext(ctx, "SELECT count(*) FROM clusters WHERE fleet_version = 'latest'")
   232  			assert.NoError(t, row.Err())
   233  			var clusterCount int
   234  			assert.NoError(t, row.Scan(&clusterCount))
   235  			assert.Equal(t, 0, clusterCount, "expected 0 clusters to have fleet_version = 'latest' after migration runs")
   236  
   237  			row = db.QueryRowContext(ctx, "SELECT count(*) FROM cluster_artifact_versions WHERE artifact_version = 'latest'")
   238  			assert.NoError(t, row.Err())
   239  			var artifactCount int
   240  			assert.NoError(t, row.Scan(&artifactCount))
   241  			assert.Equal(t, 0, artifactCount, "expected 0 cluster_artifacts to have artifact_version = 'latest' after migration runs")
   242  
   243  			return ctx
   244  		}).Feature()
   245  	f.Test(t, feat)
   246  }
   247  
   248  // insertNewLatestArtifactVersion adds the given artifact name and version to available_artifact_versions
   249  // and marks it as "latest"
   250  func insertNewLatestArtifactVersion(db *sql.DB, artifactName, artifactVersion string) error {
   251  	v := version.New()
   252  	v.SemVer = artifactVersion
   253  	major, minor, patch, err := v.SemVerMajorMinorPatch()
   254  	if err != nil {
   255  		return err
   256  	}
   257  	if _, err = db.Exec(sqlquery.AddCurrentVersionToAvailableArtifacts, artifactName, artifactVersion, major, minor, patch); err != nil {
   258  		return err
   259  	}
   260  	if _, err = db.Exec(sqlquery.UpdateLatestAvailableArtifact, artifactName, major, minor, patch); err != nil {
   261  		return err
   262  	}
   263  	return nil
   264  }
   265  

View as plain text