From 6d806a3b48047fc4553746f0ea28f0132e6a2db7 Mon Sep 17 00:00:00 2001 From: yannic rieger Date: Sat, 22 Aug 2026 22:10:53 +0200 Subject: [PATCH 1/2] only start instances when flavor version build state is COMPLETED --- controlplane/errors/error.go | 7 + controlplane/instance/service.go | 6 +- test/fixture/postgres.go | 7 + .../controlplane/instance_api_test.go | 125 ++++++++++++++++++ 4 files changed, 144 insertions(+), 1 deletion(-) diff --git a/controlplane/errors/error.go b/controlplane/errors/error.go index 8346a0bc..b5bf37c4 100644 --- a/controlplane/errors/error.go +++ b/controlplane/errors/error.go @@ -86,6 +86,13 @@ var ( ErrPrivacyPolicyNotAccepted = New(codes.FailedPrecondition, "privacy policy not accepted") ) +/* + * instance related errors + */ +var ( + ErrFlavorVersionInvalidBuildStatus = New(codes.FailedPrecondition, "flavor version build state must be COMPLETED") +) + type InvalidPathViolation struct { Field string Path string diff --git a/controlplane/instance/service.go b/controlplane/instance/service.go index 6630f6f6..1d5c2cb2 100644 --- a/controlplane/instance/service.go +++ b/controlplane/instance/service.go @@ -146,9 +146,13 @@ func (s *svc) RunFlavorVersion( version := flavor.Versions[idx] + if version.BuildStatus != resource.FlavorVersionBuildStatusCompleted { + return resource.Instance{}, apierrs.ErrFlavorVersionInvalidBuildStatus + } + ins, err := s.insRepo.CreateInstance(ctx, resource.Instance{ ID: instanceID.String(), - FlavorVersion: flavor.Versions[idx], + FlavorVersion: version, State: resource.InstanceStatePending, Owner: resource.User{ ID: u.ID, diff --git a/test/fixture/postgres.go b/test/fixture/postgres.go index 334e01a5..969628c3 100644 --- a/test/fixture/postgres.go +++ b/test/fixture/postgres.go @@ -293,6 +293,13 @@ func (p *Postgres) CreateFlavorVersion(t *testing.T, flavorID string, version *r ctx := context.Background() created, err := p.DB.CreateFlavorVersion(ctx, flavorID, *version, "") require.NoError(t, err) + + q := `UPDATE flavor_versions SET build_status = $1 WHERE id = $2` + _, err = p.Pool.Exec(ctx, q, version.BuildStatus, created.ID) + require.NoError(t, err) + + created.BuildStatus = version.BuildStatus + *version = created } diff --git a/test/functional/controlplane/instance_api_test.go b/test/functional/controlplane/instance_api_test.go index 92b9112c..bbaa906b 100644 --- a/test/functional/controlplane/instance_api_test.go +++ b/test/functional/controlplane/instance_api_test.go @@ -302,6 +302,131 @@ func TestRunFlavorVersion(t *testing.T) { } } +func TestRunFlavorVersionFailsIfFlavorVersionIsNotCompleted(t *testing.T) { + tests := []struct { + name string + status resource.FlavorVersionBuildStatus + err error + }{ + { + name: "COMPLETE runs", + status: resource.FlavorVersionBuildStatusCompleted, + }, + { + name: "CHECKPOINT_BUILD fails", + status: resource.FlavorVersionBuildStatusBuildCheckpoint, + err: apierrs.ErrFlavorVersionInvalidBuildStatus.GRPCStatus().Err(), + }, + { + name: "IMAGE_BUILD fails", + status: resource.FlavorVersionBuildStatusBuildImage, + err: apierrs.ErrFlavorVersionInvalidBuildStatus.GRPCStatus().Err(), + }, + { + name: "PENDING fails", + status: resource.FlavorVersionBuildStatusPending, + err: apierrs.ErrFlavorVersionInvalidBuildStatus.GRPCStatus().Err(), + }, + { + name: "CHECKPOINT_BUILD_FAILED fails", + status: resource.FlavorVersionBuildStatusBuildCheckpointFailed, + err: apierrs.ErrFlavorVersionInvalidBuildStatus.GRPCStatus().Err(), + }, + { + name: "IMAGE_BUILD_FAILED fails", + status: resource.FlavorVersionBuildStatusBuildImageFailed, + err: apierrs.ErrFlavorVersionInvalidBuildStatus.GRPCStatus().Err(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var ( + ctx = context.Background() + cp = fixture.NewControlPlane(t) + c = fixture.Chunk(func(tmp *resource.Chunk) { + tmp.Flavors[0].Versions[0].BuildStatus = tt.status + }) + ) + + cp.Run(t) + + cp.Postgres.InsertNode(t) + cp.Postgres.CreateChunk(t, &c, fixture.CreateOptionsAll) + + f := c.Flavors[0] + v := c.Flavors[0].Versions[0] + + fmt.Println(c.Flavors[0].Versions[0]) + + expected := &instancev1alpha1.Instance{ + Id: "", + Chunk: &chunkv1alpha1.Chunk{ + Id: c.ID, + Name: c.Name, + Description: c.Description, + Tags: c.Tags, + CreatedAt: timestamppb.New(c.CreatedAt), + UpdatedAt: timestamppb.New(c.UpdatedAt), + }, + Flavor: &chunkv1alpha1.Flavor{ + Id: f.ID, + Name: f.Name, + CreatedAt: timestamppb.New(f.CreatedAt), + UpdatedAt: timestamppb.New(f.UpdatedAt), + Versions: nil, // not returned atm + }, + FlavorVersion: &chunkv1alpha1.FlavorVersion{ + Id: v.ID, + Version: v.Version, + MinecraftVersion: fixture.MinecraftVersion, + Hash: v.Hash, + FileHashes: nil, // not returned atm + BuildStatus: chunkv1alpha1.BuildStatus(chunkv1alpha1.BuildStatus_value[string(v.BuildStatus)]), + CreatedAt: timestamppb.New(v.CreatedAt), + MinPlayers: v.MinPlayers, + MaxPlayers: v.MaxPlayers, + }, + Owner: &userv1alpha1.User{ + Id: c.Owner.ID, + Nickname: c.Owner.Nickname, + CreatedAt: timestamppb.New(c.Owner.CreatedAt), + UpdatedAt: timestamppb.New(c.Owner.UpdatedAt), + }, + Ip: fixture.Node().Addr.String(), + State: instancev1alpha1.InstanceState_PENDING, + OrderedBy: "orderer", + } + + cp.AddUserAPIKey(t, &ctx, c.Owner) + client := cp.InstanceClient(t) + + resp, err := client.RunFlavorVersion(ctx, &instancev1alpha1.RunFlavorVersionRequest{ + FlavorVersionId: v.ID, + OrderedBy: "orderer", + }) + + if tt.err != nil { + require.ErrorAs(t, err, &tt.err) + return + } + + require.NoError(t, err) + + if d := cmp.Diff( + expected, + resp.GetInstance(), + protocmp.Transform(), + test.IgnoredProtoInstanceFields, + test.IgnoredProtoFlavorVersionFields, + test.IgnoredProtoChunkFields, + test.IgnoredProtoUserFields, + ); d != "" { + t.Fatalf("diff (-want +got):\n%s", d) + } + }) + } +} + func TestDiscoverInstances(t *testing.T) { tests := []struct { name string From 0ef334f69694a8ee403c798490f6710c799baf9e Mon Sep 17 00:00:00 2001 From: yannic rieger Date: Sat, 22 Aug 2026 22:24:42 +0200 Subject: [PATCH 2/2] fix test --- test/functional/controlplane/instance_api_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/functional/controlplane/instance_api_test.go b/test/functional/controlplane/instance_api_test.go index bbaa906b..1f20d1af 100644 --- a/test/functional/controlplane/instance_api_test.go +++ b/test/functional/controlplane/instance_api_test.go @@ -214,7 +214,9 @@ func TestRunFlavorVersion(t *testing.T) { var ( ctx = context.Background() cp = fixture.NewControlPlane(t) - c = fixture.Chunk() + c = fixture.Chunk(func(tmp *resource.Chunk) { + tmp.Flavors[0].Versions[0].BuildStatus = resource.FlavorVersionBuildStatusCompleted + }) ) cp.Run(t)