-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ROX-24273: garbage collection for tenant-resources chart
- Loading branch information
Showing
5 changed files
with
299 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package reconciler | ||
|
||
//go:generate go run gen/main.go |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
// We do not install the tenant-resources using helm, so we don't have garbage collection when we delete the chart. | ||
// The main obstacle when performing garbage collection manually is that some resources might be optional (not rendered in all cases) | ||
// and we need to be able to identify them. The reconciler should be able to identify the resources that it should manage. | ||
|
||
// The purpose of this file is to | ||
// 1. Extract the apiVersions/kinds that are present in the tenant-resources chart | ||
// 2. Append to the list of apiVersion/kinds managed by the tenant-resources chart | ||
// Resources removed from the chart will not be removed from the list. This is intentional. | ||
// When rolling out a new version of the chart, the reconciler should be aware of resources | ||
// managed by previous versions of the chart. | ||
// 3. The generated file is used by the reconciler to determine the resources that it should manage | ||
// 4. If the generated file is out of date, CI should fail | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"path" | ||
"path/filepath" | ||
"regexp" | ||
"runtime" | ||
"sort" | ||
"strings" | ||
) | ||
|
||
func main() { | ||
fmt.Println("generating managed resources list for the tenant-resources chart") | ||
if err := generate(); err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
func getCurrentFile() string { | ||
_, file, _, _ := runtime.Caller(0) | ||
return file | ||
} | ||
|
||
func getChartDir() string { | ||
return path.Join(path.Dir(getCurrentFile()), "../../charts/data/tenant-resources/templates") | ||
} | ||
|
||
func getReconcilerDir() string { | ||
return path.Join(path.Dir(getCurrentFile()), "..") | ||
} | ||
|
||
func generate() error { | ||
chartDir := getChartDir() | ||
seen := map[[3]string]bool{} | ||
if err := filepath.WalkDir(chartDir, func(path string, d fs.DirEntry, err error) error { | ||
if d.IsDir() { | ||
return nil | ||
} | ||
ext := filepath.Ext(path) | ||
if ext != ".yaml" && ext != ".yml" { | ||
return nil | ||
} | ||
fileBytes, err := os.ReadFile(path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fileStr := string(fileBytes) | ||
lines := strings.Split(fileStr, "\n") | ||
for i := 0; i < len(lines)-1; i++ { | ||
line1 := lines[i] | ||
line2 := lines[i+1] | ||
if line1 > line2 { | ||
line1, line2 = line2, line1 | ||
} | ||
|
||
if !strings.HasPrefix(line1, "apiVersion: ") { | ||
continue | ||
} | ||
if !strings.HasPrefix(line2, "kind: ") { | ||
continue | ||
} | ||
apiVersion := strings.TrimSpace(strings.TrimPrefix(line1, "apiVersion: ")) | ||
kind := strings.TrimSpace(strings.TrimPrefix(line2, "kind: ")) | ||
apiVersionParts := strings.Split(apiVersion, "/") | ||
if len(apiVersionParts) > 2 { | ||
return fmt.Errorf("invalid apiVersion %s", apiVersion) | ||
} | ||
var group string | ||
var version string | ||
|
||
if len(apiVersionParts) == 2 { | ||
group = apiVersionParts[0] | ||
version = apiVersionParts[1] | ||
} else { | ||
version = apiVersionParts[0] | ||
} | ||
|
||
seen[[3]string{kind, group, version}] = true | ||
} | ||
return nil | ||
}); err != nil { | ||
return err | ||
} | ||
|
||
outFile := fmt.Sprintf("%s/zzz_managed_resources.go", getReconcilerDir()) | ||
|
||
// retrieve existing declared resources | ||
if _, err := os.Stat(outFile); err == nil { | ||
// file exists | ||
file, err := os.Open(outFile) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
|
||
// read existing file | ||
fileBytes, err := os.ReadFile(outFile) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fileStr := string(fileBytes) | ||
lines := strings.Split(fileStr, "\n") | ||
rgx := regexp.MustCompile(`schema.GroupVersionKind{Group: "(.*)", Version: "(.*)", Kind: "(.*)"},`) | ||
for _, line := range lines { | ||
matches := rgx.FindStringSubmatch(line) | ||
if len(matches) != 4 { | ||
continue | ||
} | ||
kind := matches[3] | ||
group := matches[1] | ||
version := matches[2] | ||
fmt.Printf("found existing resource: %s/%s/%s\n", kind, group, version) | ||
key := [3]string{kind, group, version} | ||
seen[key] = seen[key] | ||
} | ||
} | ||
|
||
sorted := make([][3]string, 0, len(seen)) | ||
for k := range seen { | ||
sorted = append(sorted, k) | ||
} | ||
sort.Slice(sorted, func(i, j int) bool { | ||
left := sorted[i] | ||
right := sorted[j] | ||
|
||
if left[0] != right[0] { | ||
return left[0] < right[0] | ||
} | ||
if left[1] != right[1] { | ||
return left[1] < right[1] | ||
} | ||
return left[2] < right[2] | ||
}) | ||
|
||
builder := strings.Builder{} | ||
builder.WriteString("// Code generated by fleetshard/pkg/central/reconciler/gen/main.go. DO NOT EDIT.\n") | ||
builder.WriteString("package reconciler\n\n") | ||
builder.WriteString("import (\n") | ||
builder.WriteString("\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n") | ||
builder.WriteString(")\n\n") | ||
|
||
builder.WriteString("var tenantChartResourceGVKs = []schema.GroupVersionKind{\n") | ||
for _, k := range sorted { | ||
builder.WriteString(fmt.Sprintf("\tschema.GroupVersionKind{Group: %q, Version: %q, Kind: %q},", k[1], k[2], k[0])) | ||
stillInChart := seen[k] | ||
if !stillInChart { | ||
builder.WriteString(" // This resource was present in a previous version of the chart. Manual removal is required.") | ||
} | ||
builder.WriteString("\n") | ||
} | ||
builder.WriteString("}\n") | ||
|
||
genFile, err := os.Create(outFile) | ||
if err != nil { | ||
return err | ||
} | ||
defer genFile.Close() | ||
|
||
genFile.WriteString(builder.String()) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
fleetshard/pkg/central/reconciler/zzz_managed_resources.go
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.