diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 33c490e..bd7874b 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -140,7 +140,7 @@ func main() { // LeaderElectionReleaseOnCancel: true, }) if err != nil { - setupLog.Error(err, "unable to start manager: %s", err.Error()) + setupLog.Error(err, "unable to start manager: "+err.Error()) os.Exit(1) } diff --git a/internal/pkg/utils.go b/internal/pkg/utils.go index d39446c..5102ae6 100644 --- a/internal/pkg/utils.go +++ b/internal/pkg/utils.go @@ -6,6 +6,7 @@ import ( "reflect" "strings" + f5_bigip "github.com/f5devcentral/f5-bigip-rest-go/bigip" "github.com/f5devcentral/f5-bigip-rest-go/deployer" "github.com/f5devcentral/f5-bigip-rest-go/utils" v1 "k8s.io/api/core/v1" @@ -217,6 +218,15 @@ func DeployForEvent(ctx context.Context, impactedClasses []string, apply func() } } + // TODO: fix the issue: + // 2023/06/19 09:26:49.824036 [ERROR] [cd7c411d-e392-424f-8a76-be055f1286d2] \ + // failed to deploy partition cis-c-tenant: 400, {"code":400,"message":"0107082a:3: \ + // All objects must be removed from a partition (cis-c-tenant) before the partition may be removed, type ID (973)","errorStack":[],"apiError":3} + + // TODO: fix the issue: + // 2023/06/19 09:17:39.572853 [ERROR] [a763bd16-498a-415a-89a3-f5fdf2aa5adf] \ + // failed to do deployment to https://10.250.15.109:443: 400, {"code":400,"message":"transaction failed:01070110:3: \ + // Node address '/cis-c-tenant/10.250.16.103' is referenced by a member of pool '/cis-c-tenant/default.dev-service'.","errorStack":[],"apiError":2} drs["cis-c-tenant"] = &deployer.DeployRequest{ Meta: fmt.Sprintf("Updating pools for event %s", meta), From: &opcfgs, @@ -291,3 +301,67 @@ func validateSecretType(group *gatewayv1beta1.Group, kind *gatewayv1beta1.Kind) } return nil } + +// purgeCommonNodes tries to remove nodes from Common if no reference. +func purgeCommonNodes(ctx context.Context, ombs []interface{}) { + for _, bp := range BIGIPs { + bc := f5_bigip.BIGIPContext{Context: ctx, BIGIP: *bp} + slog := utils.LogFromContext(ctx) + + for _, m := range ombs { + partition := m.(map[string]interface{})["partition"].(string) + if partition != "Common" { + continue + } + addr := m.(map[string]interface{})["address"].(string) + err := bc.Delete("ltm/node", addr, "Common", "") + if err != nil && !strings.Contains(err.Error(), "is referenced by a member of pool") { + slog.Warnf("cannot delete node %s: %s", addr, err.Error()) + } + } + } +} + +// // splitByPartition split the cfgs into a map of which keys are partitions +// func splitByPartition(ctx context.Context, cfgs map[string]interface{}) map[string]interface{} { +// partitions := map[string]map[string]map[string]interface{}{} +// for fstr, fv := range cfgs { +// for rstr, rv := range fv.(map[string]interface{}) { +// pstr := "unknown" +// if p, f := rv.(map[string]interface{})["partition"]; f { +// pstr = p.(string) +// } + +// if _, pok := partitions[pstr]; !pok { +// partitions[pstr] = map[string]map[string]interface{}{} + +// } +// if _, fok := partitions[pstr][fstr]; !fok { +// partitions[pstr][fstr] = map[string]interface{}{} +// } +// partitions[pstr][fstr][rstr] = rv +// } +// } +// rlt := map[string]interface{}{} +// for p, v := range partitions { +// rlt[p] = v +// } +// return rlt +// } + +// filterCommonResources filter the 'Common' resources from cfgs +func filterCommonResources(cfgs map[string]interface{}) map[string]interface{} { + rlt := map[string]interface{}{} + for fstr, fv := range cfgs { + if _, ok := rlt[fstr]; !ok { + rlt[fstr] = map[string]interface{}{} + } + for rstr, rv := range fv.(map[string]interface{}) { + if p, f := rv.(map[string]interface{})["partition"]; f && p == "Common" { + rlt[fstr].(map[string]interface{})[rstr] = rv + delete(cfgs[fstr].(map[string]interface{}), rstr) + } + } + } + return rlt +} diff --git a/internal/pkg/utils_test.go b/internal/pkg/utils_test.go index 05b411f..991a2db 100644 --- a/internal/pkg/utils_test.go +++ b/internal/pkg/utils_test.go @@ -101,3 +101,78 @@ func TestReferenceGrantFromTo_ops(t *testing.T) { } }) } + +func Test_filterCommonResources(t *testing.T) { + type args struct { + cfgs map[string]interface{} + } + tests := []struct { + name string + args args + want map[string]interface{} + left map[string]interface{} + }{ + { + name: "normal test", + args: args{ + cfgs: map[string]interface{}{ + "": map[string]interface{}{ + "ltm/pool/a": map[string]interface{}{ + "name": "a", + "partition": "cis-c-tenant", + }, + "ltm/pool/b": map[string]interface{}{ + "name": "b", + "partition": "Common", + }, + }, + "f": map[string]interface{}{ + "ltm/pool/c": map[string]interface{}{ + "name": "c", + "partition": "cis-c-tenant", + }, + "ltm/pool/d": map[string]interface{}{ + "name": "d", + "partition": "Common", + }, + }, + }, + }, + want: map[string]interface{}{ + "": map[string]interface{}{ + "ltm/pool/b": map[string]interface{}{ + "name": "b", + "partition": "Common", + }, + }, + "f": map[string]interface{}{ + "ltm/pool/d": map[string]interface{}{ + "name": "d", + "partition": "Common", + }, + }, + }, + left: map[string]interface{}{ + "": map[string]interface{}{ + "ltm/pool/a": map[string]interface{}{ + "name": "a", + "partition": "cis-c-tenant", + }, + }, + "f": map[string]interface{}{ + "ltm/pool/c": map[string]interface{}{ + "name": "c", + "partition": "cis-c-tenant", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := filterCommonResources(tt.args.cfgs); !reflect.DeepEqual(got, tt.want) || !reflect.DeepEqual(tt.args.cfgs, tt.left) { + t.Errorf("filterCommonResources() = %v, want %v, left %v", got, tt.want, tt.left) + } + }) + } +}