Skip to content

Commit a221125

Browse files
committed
controller: support metadata.project in app/comp/vm
1 parent a634092 commit a221125

File tree

3 files changed

+124
-7
lines changed

3 files changed

+124
-7
lines changed

cmd/cli/get.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,8 @@ var getCmd = &cobra.Command{
102102
fmt.Printf("\n")
103103
}
104104

105-
// DB-backed VMs are shown in controller mode unless CloudStack-scoped
106-
// VM listing is explicitly requested.
107-
if resourceType == "VirtualMachine" && !getAllVMs && getProject == "" && !getAllProjects {
105+
// DB-backed VMs are shown in controller mode unless --all-vms is set.
106+
if resourceType == "VirtualMachine" && !getAllVMs {
108107
var vms []v1.VirtualMachine
109108
if err := json.Unmarshal(body, &vms); err == nil {
110109
handlers.PrintVMsFromController(vms)

controller/controller.go

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"log"
1212
"net/http"
1313
"reflect"
14+
"strings"
1415
"sync"
1516
"time"
1617

@@ -487,6 +488,11 @@ func (c *Controller) handleList(w http.ResponseWriter, r *http.Request) {
487488
if appFilter != "" {
488489
q = q.Where("name = ?", appFilter)
489490
}
491+
if projectFilter != "" {
492+
q = q.Where("metadata_project = ?", projectFilter)
493+
} else if !allProjects {
494+
q = q.Where("metadata_project IS NULL OR metadata_project = ''")
495+
}
490496
if err := q.Find(&apps).Error; err != nil {
491497
http.Error(w, "failed to list applications", http.StatusInternalServerError)
492498
return
@@ -506,6 +512,29 @@ func (c *Controller) handleList(w http.ResponseWriter, r *http.Request) {
506512
if appFilter != "" {
507513
q = q.Where("application = ?", appFilter)
508514
}
515+
if projectFilter != "" {
516+
var appNames []string
517+
if err := db.DB.Model(&v1.Application{}).Where("metadata_project = ?", projectFilter).Pluck("name", &appNames).Error; err != nil {
518+
http.Error(w, "failed to filter components by project", http.StatusInternalServerError)
519+
return
520+
}
521+
if len(appNames) > 0 {
522+
q = q.Where("application IN ?", appNames)
523+
} else {
524+
q = q.Where("(application IS NULL OR application = '') AND metadata_project = ?", projectFilter)
525+
}
526+
} else if !allProjects {
527+
var appNames []string
528+
if err := db.DB.Model(&v1.Application{}).Where("metadata_project IS NULL OR metadata_project = ''").Pluck("name", &appNames).Error; err != nil {
529+
http.Error(w, "failed to filter components by default project scope", http.StatusInternalServerError)
530+
return
531+
}
532+
if len(appNames) > 0 {
533+
q = q.Where("application IN ? OR ((application IS NULL OR application = '') AND (metadata_project IS NULL OR metadata_project = ''))", appNames)
534+
} else {
535+
q = q.Where("(application IS NULL OR application = '') AND (metadata_project IS NULL OR metadata_project = '')")
536+
}
537+
}
509538
if err := q.Find(&comps).Error; err != nil {
510539
http.Error(w, "failed to list components", http.StatusInternalServerError)
511540
return
@@ -516,7 +545,7 @@ func (c *Controller) handleList(w http.ResponseWriter, r *http.Request) {
516545
w.Write(b)
517546
return
518547
case "VirtualMachine":
519-
if r.URL.Query().Get("all-vms") == "true" || projectFilter != "" || allProjects {
548+
if r.URL.Query().Get("all-vms") == "true" {
520549
payload := map[string]interface{}{"kind": "VirtualMachine"}
521550
if name != "" {
522551
payload["name"] = name
@@ -549,6 +578,29 @@ func (c *Controller) handleList(w http.ResponseWriter, r *http.Request) {
549578
if appFilter != "" {
550579
q = q.Where("application = ?", appFilter)
551580
}
581+
if projectFilter != "" {
582+
var appNames []string
583+
if err := db.DB.Model(&v1.Application{}).Where("metadata_project = ?", projectFilter).Pluck("name", &appNames).Error; err != nil {
584+
http.Error(w, "failed to filter virtualmachines by project", http.StatusInternalServerError)
585+
return
586+
}
587+
if len(appNames) > 0 {
588+
q = q.Where("application IN ? OR ((application IS NULL OR application = '') AND metadata_project = ?)", appNames, projectFilter)
589+
} else {
590+
q = q.Where("(application IS NULL OR application = '') AND metadata_project = ?", projectFilter)
591+
}
592+
} else if !allProjects {
593+
var appNames []string
594+
if err := db.DB.Model(&v1.Application{}).Where("metadata_project IS NULL OR metadata_project = ''").Pluck("name", &appNames).Error; err != nil {
595+
http.Error(w, "failed to filter virtualmachines by default project scope", http.StatusInternalServerError)
596+
return
597+
}
598+
if len(appNames) > 0 {
599+
q = q.Where("application IN ? OR ((application IS NULL OR application = '') AND (metadata_project IS NULL OR metadata_project = ''))", appNames)
600+
} else {
601+
q = q.Where("(application IS NULL OR application = '') AND (metadata_project IS NULL OR metadata_project = '')")
602+
}
603+
}
552604
if err := q.Find(&vms).Error; err != nil {
553605
http.Error(w, "failed to list virtualmachines", http.StatusInternalServerError)
554606
return
@@ -940,6 +992,23 @@ func (c *Controller) Apply(resource interface{}) error {
940992

941993
// applyApplication creates/updates an Application resource
942994
func (c *Controller) applyApplication(app *v1.Application) error {
995+
// Accept project in either metadata.project or spec.project and keep them in sync.
996+
appProject := strings.TrimSpace(app.Metadata.Project)
997+
if appProject == "" {
998+
appProject = strings.TrimSpace(app.Spec.Project)
999+
}
1000+
if appProject != "" {
1001+
app.Metadata.Project = appProject
1002+
app.Spec.Project = appProject
1003+
}
1004+
1005+
// Upsert: look up an existing record by name so Save() performs an UPDATE
1006+
// rather than an INSERT (Save with ID=0 always inserts a new row).
1007+
var existing v1.Application
1008+
if err := db.DB.Where("name = ?", app.Metadata.Name).First(&existing).Error; err == nil {
1009+
app.Model = existing.Model
1010+
}
1011+
9431012
// Save desired state to database and mark as Starting.
9441013
if app.Status.ObservedState == "" {
9451014
app.Status.ObservedState = "Starting"
@@ -963,14 +1032,19 @@ func (c *Controller) applyApplication(app *v1.Application) error {
9631032
// referenced by the Application if they do not already exist. It also ensures
9641033
// the Component.Application field is set to the owning application name.
9651034
func ensureComponentsForApplication(app *v1.Application) error {
1035+
appProject := strings.TrimSpace(app.Metadata.Project)
1036+
if appProject == "" {
1037+
appProject = strings.TrimSpace(app.Spec.Project)
1038+
}
1039+
9661040
for _, cref := range app.Spec.Components {
9671041
var comp v1.Component
9681042
if err := db.DB.Where("name = ?", cref.Name).First(&comp).Error; err != nil {
9691043
// component not found: create a new record
9701044
comp = v1.Component{
9711045
APIVersion: v1.APIVersion,
9721046
Kind: "Component",
973-
Metadata: v1.Metadata{Name: cref.Name},
1047+
Metadata: v1.Metadata{Name: cref.Name, Project: appProject},
9741048
Spec: v1.ComponentSpec{
9751049
VirtualMachineSpec: cref.VirtualMachineSpec,
9761050
Replicas: cref.Replicas,
@@ -1011,14 +1085,37 @@ func ensureComponentsForApplication(app *v1.Application) error {
10111085
}
10121086
}
10131087

1088+
if appProject != "" && comp.Metadata.Project != appProject {
1089+
if err := db.DB.Model(&v1.Component{}).Where("name = ?", comp.Metadata.Name).Update("metadata_project", appProject).Error; err != nil {
1090+
log.Printf("failed to set component %s project to %s: %v", comp.Metadata.Name, appProject, err)
1091+
return err
1092+
}
1093+
comp.Metadata.Project = appProject
1094+
}
1095+
10141096
// Also ensure existing VM records for this component are linked
10151097
// to the owning application if they don't already have an owner.
10161098
var vms []v1.VirtualMachine
10171099
if err := db.DB.Where("component = ?", comp.Metadata.Name).Find(&vms).Error; err == nil {
10181100
for _, vm := range vms {
1101+
changed := false
10191102
if vm.Application == "" {
10201103
log.Printf("Linking VM %s to application %s", vm.Metadata.Name, app.Metadata.Name)
1021-
if err := db.DB.Model(&v1.VirtualMachine{}).Where("name = ?", vm.Metadata.Name).Update("application", app.Metadata.Name).Error; err != nil {
1104+
vm.Application = app.Metadata.Name
1105+
changed = true
1106+
}
1107+
if appProject != "" {
1108+
if vm.Metadata.Project != appProject {
1109+
vm.Metadata.Project = appProject
1110+
changed = true
1111+
}
1112+
if vm.Spec.Project != appProject {
1113+
vm.Spec.Project = appProject
1114+
changed = true
1115+
}
1116+
}
1117+
if changed {
1118+
if err := db.DB.Save(&vm).Error; err != nil {
10221119
return err
10231120
}
10241121
}
@@ -1061,6 +1158,8 @@ func (c *Controller) applyComponent(comp *v1.Component) error {
10611158
if existing.Application != "" && comp.Application == "" {
10621159
comp.Application = existing.Application
10631160
}
1161+
// Use the existing record's primary key so Save() performs UPDATE
1162+
comp.Model = existing.Model
10641163
}
10651164

10661165
// Persist desired component with effective spec

controller/reconcile.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,22 @@ func (c *Controller) createComponentVMs(appName string, comp *v1.Component, comp
510510
for _, vmName := range vmNames {
511511
var existing v1.VirtualMachine
512512
if err := db.DB.Where("name = ?", vmName).First(&existing).Error; err == nil {
513+
changed := false
514+
if comp.Metadata.Project != "" {
515+
if existing.Metadata.Project != comp.Metadata.Project {
516+
existing.Metadata.Project = comp.Metadata.Project
517+
changed = true
518+
}
519+
if existing.Spec.Project != comp.Metadata.Project {
520+
existing.Spec.Project = comp.Metadata.Project
521+
changed = true
522+
}
523+
}
524+
if changed {
525+
if err := db.DB.Save(&existing).Error; err != nil {
526+
return err
527+
}
528+
}
513529
// already exists, add pointer to slice
514530
e := existing
515531
vms = append(vms, &e)
@@ -520,10 +536,13 @@ func (c *Controller) createComponentVMs(appName string, comp *v1.Component, comp
520536
vm := &v1.VirtualMachine{
521537
APIVersion: v1.APIVersion,
522538
Kind: "VirtualMachine",
523-
Metadata: v1.Metadata{Name: vmName},
539+
Metadata: v1.Metadata{Name: vmName, Project: comp.Metadata.Project},
524540
Spec: effective,
525541
Component: comp.Metadata.Name,
526542
}
543+
if vm.Spec.Project == "" {
544+
vm.Spec.Project = comp.Metadata.Project
545+
}
527546
if appName != "" {
528547
vm.Application = appName
529548
}

0 commit comments

Comments
 (0)