feat(server): grandfather existing cloud instances onto Professional
This commit is contained in:
@@ -59,6 +59,13 @@ func main() {
|
||||
log.Fatalf("scoped collection check failed: %v", assertErr)
|
||||
}
|
||||
|
||||
gfCtx, gfCancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
gfErr := services.MigrateGrandfatherLicences(gfCtx, db.Database)
|
||||
gfCancel()
|
||||
if gfErr != nil {
|
||||
log.Fatalf("licence grandfather migration failed: %v", gfErr)
|
||||
}
|
||||
|
||||
if err := services.EnsureAuthIndexes(); err != nil {
|
||||
log.Fatalf("failed to ensure auth indexes: %v", err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/mrhid6/vantage/shared/license"
|
||||
"github.com/mrhid6/vantage/shared/models"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.mongodb.org/mongo-driver/v2/mongo"
|
||||
)
|
||||
|
||||
// MigrateGrandfatherLicences stores pre-issued licences on instances that have
|
||||
// none. Cloud only.
|
||||
//
|
||||
// The blobs are supplied through VANTAGE_GRANDFATHER_BLOBS, a JSON object
|
||||
// mapping instance_id to licence blob, because this process cannot sign: it
|
||||
// holds no private key and the signing code is compiled out. Cut the blobs
|
||||
// beforehand with lkctl.
|
||||
//
|
||||
// The variable is single-use. Unset it on the next deploy.
|
||||
func MigrateGrandfatherLicences(ctx context.Context, db *mongo.Database) error {
|
||||
const marker = "0005_grandfather_licences"
|
||||
|
||||
if n, _ := db.Collection("migrations").CountDocuments(ctx, bson.M{"_id": marker}); n > 0 {
|
||||
return nil
|
||||
}
|
||||
if DeploymentMode() != license.DeploymentCloud {
|
||||
log.Printf("0005: not a cloud deployment, skipping")
|
||||
return nil
|
||||
}
|
||||
|
||||
raw := os.Getenv("VANTAGE_GRANDFATHER_BLOBS")
|
||||
if raw == "" {
|
||||
log.Printf("0005: VANTAGE_GRANDFATHER_BLOBS not set, skipping (no marker recorded)")
|
||||
return nil
|
||||
}
|
||||
|
||||
var blobs map[string]string
|
||||
if err := json.Unmarshal([]byte(raw), &blobs); err != nil {
|
||||
return fmt.Errorf("0005: VANTAGE_GRANDFATHER_BLOBS is not valid JSON: %w", err)
|
||||
}
|
||||
|
||||
cur, err := db.Collection("instances").Find(ctx, bson.M{
|
||||
"$or": []bson.M{
|
||||
{"license_blob": bson.M{"$exists": false}},
|
||||
{"license_blob": ""},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("0005: list instances: %w", err)
|
||||
}
|
||||
var instances []models.Instance
|
||||
if err := cur.All(ctx, &instances); err != nil {
|
||||
return fmt.Errorf("0005: decode instances: %w", err)
|
||||
}
|
||||
|
||||
var stored, missing int
|
||||
for _, inst := range instances {
|
||||
blob, ok := blobs[inst.InstanceID]
|
||||
if !ok || blob == "" {
|
||||
log.Printf("0005: no blob supplied for instance %s (%s)", inst.InstanceID, inst.Slug)
|
||||
missing++
|
||||
continue
|
||||
}
|
||||
|
||||
res := license.Verify(blob, license.VerifyOpts{
|
||||
InstanceID: inst.InstanceID,
|
||||
Deployment: license.DeploymentCloud,
|
||||
})
|
||||
if res.State == license.StateInvalid {
|
||||
return fmt.Errorf("0005: blob for instance %s is rejected: %s", inst.InstanceID, res.Reason)
|
||||
}
|
||||
|
||||
if _, err := db.Collection("instances").UpdateOne(ctx,
|
||||
bson.M{"instance_id": inst.InstanceID},
|
||||
bson.M{"$set": bson.M{
|
||||
"license_blob": blob,
|
||||
"license_tier": res.License.Tier,
|
||||
"license_expiry": res.License.ExpiresAt,
|
||||
}}); err != nil {
|
||||
return fmt.Errorf("0005: store blob for %s: %w", inst.InstanceID, err)
|
||||
}
|
||||
log.Printf("0005: stored %s licence for instance %s (%s), expires %s",
|
||||
res.License.Tier, inst.InstanceID, inst.Slug,
|
||||
res.License.ExpiresAt.Format(time.RFC3339))
|
||||
stored++
|
||||
}
|
||||
|
||||
if missing > 0 {
|
||||
return fmt.Errorf("0005: %d instance(s) had no blob supplied; issue them with lkctl and rerun", missing)
|
||||
}
|
||||
|
||||
_, err = db.Collection("migrations").InsertOne(ctx,
|
||||
bson.M{"_id": marker, "applied_at": time.Now()})
|
||||
log.Printf("0005: grandfathered %d instance(s)", stored)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user