package integration_test import ( "context" "strings" "github.com/udacity/graphb" "edge-infra.dev/pkg/edge/api/graph/model" testApi "edge-infra.dev/pkg/edge/api/graph/test" "edge-infra.dev/pkg/edge/api/services" "edge-infra.dev/pkg/edge/api/types" "edge-infra.dev/pkg/edge/api/utils" "edge-infra.dev/pkg/edge/constants/api/cluster" "edge-infra.dev/pkg/edge/constants/api/fleet" "edge-infra.dev/pkg/lib/runtime/version" "edge-infra.dev/test/framework/integration" ) func (s *Suite) TestRegisterCluster() { integration.SkipIf(s.Framework) createSite := true var response struct{ RegisterCluster *model.RegistrationResponse } mutation := registerClusterMutation(&model.RegistrationPayload{ Name: testStore, Fleet: fleet.Store, ClusterType: cluster.GKE, StoreInfo: &model.StoreInfo{ StoreID: &testStoreID, //using store name as store id, until store id is properly defined CreateSite: &createSite, }, BannerName: testOrg, ClusterInfo: &model.ClusterInfo{ Location: testLocation, NodeVersion: &testClusterVersion, MachineType: testClusterMachineType, NumNodes: testClusterNumNodes, }, }) ResolverClient.MustPost(mutation, &response) s.NotNil(response.RegisterCluster) s.NotEmpty(response.RegisterCluster.ClusterEdgeID) s.Equal(*response.RegisterCluster.SiteID, testSiteID) } func (s *Suite) TestRegisterDSDSCluster() { store := "dsds-cluster" testDSDSCluster(s, store, cluster.DSDS) } func testDSDSCluster(s *Suite, store, clusterProvider string) { integration.SkipIf(s.Framework) createSite := true var response struct{ RegisterCluster *model.RegistrationResponse } mutation := registerClusterMutation(&model.RegistrationPayload{ Name: store, Fleet: fleet.Store, ClusterType: clusterProvider, StoreInfo: &model.StoreInfo{ StoreID: &testStoreID, //using store name as store id, until store id is properly defined CreateSite: &createSite, }, BannerName: testOrg, ClusterInfo: &model.ClusterInfo{ Location: testLocation, NodeVersion: &testClusterVersion, MachineType: testClusterMachineType, NumNodes: testClusterNumNodes, }, }) err := ResolverClient.Post(mutation, &response) s.NoError(err) s.NotNil(response) s.NotNil(response.RegisterCluster) s.NotEmpty(response.RegisterCluster.ClusterEdgeID) s.Equal(*response.RegisterCluster.SiteID, testSiteID) clusterEdgeID := response.RegisterCluster.ClusterEdgeID var clusterConfigResponse struct{ ClusterConfig *model.ClusterConfig } query := getClusterConfigQuery(clusterEdgeID) err = ResolverClient.Post(query, &clusterConfigResponse) s.NoError(err) s.NotNil(clusterConfigResponse) s.NotNil(clusterConfigResponse.ClusterConfig) s.Equal(model.ClusterConfig{ ClusterEdgeID: clusterEdgeID, AcRelay: false, VpnEnabled: false, BootstrapAck: true, PxeEnabled: false, ThickPos: true, EgressGatewayEnabled: false, }, *clusterConfigResponse.ClusterConfig) } func (s *Suite) TestRegisterClusterByBannerEdgeID() { integration.SkipIf(s.Framework) createSite := true var response struct{ RegisterCluster *model.RegistrationResponse } mutation := registerClusterMutation(&model.RegistrationPayload{ Name: "banner_edge_id_store", Fleet: fleet.Store, ClusterType: cluster.GKE, StoreInfo: &model.StoreInfo{ StoreID: &testStoreID, //using store name as store id, until store id is properly defined CreateSite: &createSite, }, BannerName: testOrg, BannerEdgeID: &testApi.TestOrgEdgeID, ClusterInfo: &model.ClusterInfo{ Location: testLocation, NodeVersion: &testClusterVersion, MachineType: testClusterMachineType, NumNodes: testClusterNumNodes, }, }) ResolverClient.MustPost(mutation, &response) s.NotNil(response.RegisterCluster) s.NotEmpty(response.RegisterCluster.ClusterEdgeID) s.Equal(*response.RegisterCluster.SiteID, testSiteID) } func (s *Suite) TestRegisterExistingCluster() { integration.SkipIf(s.Framework) createSite := true var response struct{ RegisterCluster *model.RegistrationResponse } mutation := registerClusterMutation(&model.RegistrationPayload{ Name: "test_cluster-status", Fleet: fleet.Store, ClusterType: cluster.GKE, StoreInfo: &model.StoreInfo{ StoreID: &testStoreID, CreateSite: &createSite, }, BannerName: "store-status-banner", ClusterInfo: &model.ClusterInfo{ Location: testLocation, NodeVersion: &testClusterVersion, MachineType: testClusterMachineType, NumNodes: testClusterNumNodes, }, }) err := ResolverClient.Post(mutation, &response) s.Error(err) s.ErrorContains(err, "[{\"message\":\"cluster already registered, please try again with a different name. cluster name: test_cluster-status\",\"path\":[\"registerCluster\"]}]") } func (s *Suite) TestRegister_ReuseClusterName() { integration.SkipIf(s.Framework) createSite := true request := &model.RegistrationPayload{ Name: "test-register-reuse-name", Fleet: fleet.Store, ClusterType: cluster.DSDS, StoreInfo: &model.StoreInfo{ StoreID: &testStoreID, CreateSite: &createSite, }, BannerName: testOrg, BannerEdgeID: &testApi.TestOrgEdgeID, ClusterInfo: &model.ClusterInfo{ Location: testLocation, NodeVersion: &testClusterVersion, MachineType: testClusterMachineType, NumNodes: testClusterNumNodes, }, } // register cluster named "test-register-reuse-name" var response1 struct{ RegisterCluster *model.RegistrationResponse } s.NoError( ResolverClient.Post(registerClusterMutation(request), &response1), "unexpected error registering cluster. first registration with unique name should succeed", ) clusterID1 := response1.RegisterCluster.ClusterEdgeID // registering cluster again with same name should fail var response2 struct{ RegisterCluster *model.RegistrationResponse } s.Error( ResolverClient.Post(registerClusterMutation(request), &response2), "error registering cluster. second registration with same name should fail", ) s.Equal(clusterID1, response2.RegisterCluster.ClusterEdgeID, "expected registration response to return id of existing cluster") // delete the cluster to free up name var deleteResponse struct{ DeleteCluster *bool } s.NoError( ResolverClient.Post(deleteClusterQuery(clusterID1, false), &deleteResponse), "unexpected error deleting cluster", ) s.Require().NotNil(deleteResponse.DeleteCluster, "expected deleteCluster to return true, was nil") s.True(*deleteResponse.DeleteCluster, "expected deleteCluster to return true") // registering cluster named "test-register-reuse-name" again should now succeed var response3 struct{ RegisterCluster *model.RegistrationResponse } s.NoError( ResolverClient.Post(registerClusterMutation(request), &response3), "unexpected error registering cluster. registration should succeed when reusing deleted cluster name", ) clusterID3 := response3.RegisterCluster.ClusterEdgeID s.NotEqual(clusterID1, clusterID3, "cluster_edge_ids should differ when reusing old cluster name for new registration") } func (s *Suite) TestRegisterClusterCreateSQLEntryError() { integration.SkipIf(s.Framework) createSite := true var response struct{ RegisterCluster *model.RegistrationResponse } mutation := registerClusterMutation(&model.RegistrationPayload{ Name: "test-store-2", Fleet: fleet.Store, ClusterType: "unknown type", StoreInfo: &model.StoreInfo{ StoreID: &testStoreID, CreateSite: &createSite, }, BannerName: testOrg, ClusterInfo: &model.ClusterInfo{ Location: testLocation, NodeVersion: &testClusterVersion, MachineType: testClusterMachineType, NumNodes: testClusterNumNodes, }, }) err := ResolverClient.Post(mutation, &response) s.Error(err) s.False(strings.Contains(err.Error(), "rollback errors")) // test if the previous site has been rolled back createSite = true mutation = registerClusterMutation(&model.RegistrationPayload{ Name: "test-store-2", Fleet: fleet.Store, ClusterType: cluster.GKE, StoreInfo: &model.StoreInfo{ StoreID: &testStoreID, CreateSite: &createSite, }, BannerName: testOrg, ClusterInfo: &model.ClusterInfo{ Location: testLocation, NodeVersion: &testClusterVersion, MachineType: testClusterMachineType, NumNodes: testClusterNumNodes, }, }) ResolverClient.MustPost(mutation, &response) s.NotNil(response.RegisterCluster) s.NotEmpty(response.RegisterCluster.ClusterEdgeID) s.Equal(*response.RegisterCluster.SiteID, testSiteID) } func (s *Suite) TestRegisterClusterCreateBanner() { integration.SkipIf(s.Framework) createSite := false var response struct{ RegisterCluster *model.RegistrationResponse } mutation := registerClusterMutation(&model.RegistrationPayload{ Name: "test-store-3", Fleet: fleet.Cluster, ClusterType: cluster.GKE, StoreInfo: &model.StoreInfo{ StoreID: &testStoreID, CreateSite: &createSite, }, BannerName: testOrg, ClusterInfo: &model.ClusterInfo{ Location: testLocation, NodeVersion: &testClusterVersion, MachineType: testClusterMachineType, NumNodes: testClusterNumNodes, }, }) err := ResolverClient.Post(mutation, &response) s.NoError(err) s.NotNil(response.RegisterCluster) s.NotEmpty(response.RegisterCluster.ClusterEdgeID) s.Equal(*response.RegisterCluster.SiteID, "") } func (s *Suite) TestRegisterCluster_AutoUpdateBackCompat() { integration.SkipIf(s.Framework) createSite := false var response struct{ RegisterCluster *model.RegistrationResponse } latest := types.DefaultVersionTag mutation := registerClusterMutation(&model.RegistrationPayload{ Name: "test-store-4", Fleet: fleet.Store, FleetVersion: &latest, ClusterType: cluster.Generic, StoreInfo: &model.StoreInfo{ StoreID: &testStoreID, CreateSite: &createSite, }, BannerName: testOrg, ClusterInfo: &model.ClusterInfo{ Location: testLocation, NodeVersion: &testClusterVersion, MachineType: testClusterMachineType, NumNodes: testClusterNumNodes, }, }) err := ResolverClient.Post(mutation, &response) s.NoError(err) s.NotNil(response.RegisterCluster) newClusterQuery := graphb.Query{ Type: graphb.TypeQuery, Fields: []*graphb.Field{ { Name: "cluster", Arguments: []graphb.Argument{ graphb.ArgumentString("clusterEdgeId", response.RegisterCluster.ClusterEdgeID), }, Fields: []*graphb.Field{ graphb.NewField("fleetVersion"), }, }, }, } var clusterResponse struct{ Cluster *model.Cluster } s.NoError(ResolverClient.Post(MustParse(newClusterQuery), &clusterResponse)) s.NotNil(clusterResponse.Cluster) s.Equal(version.New().SemVer, clusterResponse.Cluster.FleetVersion) } func registerClusterMutation(payload *model.RegistrationPayload) string { if payload.FleetVersion == nil { version := types.DefaultVersionTag payload.FleetVersion = &version } if payload.AutoUpdateEnabled == nil { autoUpdate := false payload.AutoUpdateEnabled = &autoUpdate } return MustParse(graphb.Query{ Type: graphb.TypeMutation, Fields: []*graphb.Field{ { Name: "registerCluster", Arguments: []graphb.Argument{ graphb.ArgumentCustomType("payload", graphb.ArgumentString("name", payload.Name), graphb.ArgumentString("fleet", payload.Fleet), graphb.ArgumentString("fleetVersion", *payload.FleetVersion), graphb.ArgumentBool("autoUpdateEnabled", *payload.AutoUpdateEnabled), graphb.ArgumentString("clusterType", payload.ClusterType), graphb.ArgumentString("bannerName", payload.BannerName), graphb.ArgumentCustomType("clusterInfo", graphb.ArgumentString("location", payload.ClusterInfo.Location), graphb.ArgumentString("machineType", payload.ClusterInfo.MachineType), graphb.ArgumentString("nodeVersion", *payload.ClusterInfo.NodeVersion), graphb.ArgumentInt("numNodes", payload.ClusterInfo.NumNodes), graphb.ArgumentBool("autoscale", payload.ClusterInfo.Autoscale), graphb.ArgumentInt("minNodes", utils.ConvertToInt(payload.ClusterInfo.MinNodes)), graphb.ArgumentInt("maxNodes", utils.ConvertToInt(payload.ClusterInfo.MaxNodes)), ), graphb.ArgumentCustomType("storeInfo", graphb.ArgumentString("storeID", *payload.StoreInfo.StoreID), graphb.ArgumentBool("createSite", *payload.StoreInfo.CreateSite), ), ), }, Fields: graphb.Fields("clusterEdgeId", "siteId"), }, }, }) } const ( // for use in registration_queries.resolvers_test.go (cluster reset) resetClusterEdgeID = "076b3f30-7c78-4b19-9d43-f8f476ab0d58" ) func (s *Suite) TestResetStoreClusterForce() { integration.SkipIf(s.Framework) var response struct{ ResetCluster bool } mutation := resetClusterMutation(resetClusterEdgeID, true) err := ResolverClient.Post(mutation, &response) s.NoError(err) cluster, err := s.Resolver.StoreClusterService.GetCluster(context.Background(), resetClusterEdgeID) s.NoError(err) s.NotNil(cluster.Active) s.False(*cluster.Active) // cluster should be marked inactive } func (s *Suite) TestResetStoreClusterNoForce() { integration.SkipIf(s.Framework) var response struct{ ResetCluster bool } mutation := resetClusterMutation(resetClusterEdgeID, false) err := ResolverClient.Post(mutation, &response) s.ErrorContains(err, services.ErrMustForceClusterReset.Error()) } func (s *Suite) TestResetNonStoreClusterForce() { integration.SkipIf(s.Framework) clusterInfraID := "3396a52c-6a22-4049-9593-5a63b596a200" var response struct{ ResetCluster bool } mutation := resetClusterMutation(clusterInfraID, true) err := ResolverClient.Post(mutation, &response) s.ErrorContains(err, services.ErrCanOnlyResetStore.Error()) } func resetClusterMutation(clusterEdgeID string, force bool) string { return MustParse(graphb.Query{ Type: graphb.TypeMutation, Fields: []*graphb.Field{ { Name: "resetCluster", Arguments: []graphb.Argument{ graphb.ArgumentString("clusterEdgeId", clusterEdgeID), graphb.ArgumentBool("force", force), }, }, }, }) }