1
0
mirror of https://github.com/rancher/steve.git synced 2025-09-07 18:31:13 +00:00

Hard-wire external associations: 3 sections in, this one is 4/7 (#645)

* Continue rebasing.

* Wrote unit tests for external associations.

* Fix the generated SQL.

Some syntactic sugar (capitalizing the keywords), but use the 'ON' syntax on JOINs.

* whitespace fix post rebase

* We want "management.cattle.io.projects:spec.displayName" not "...spec.clusterName"

* Fix the database calls: drop the key

* Fix breakage during automatic rebase merging gone wrong.

* ws fix - NFC

* Post rebase-merge fixes

* Fix rebase-driven merge.

* Fix rebase breakage.

* go-uber v0.5.2 prefers real arg names to '<argN>'
This commit is contained in:
Eric Promislow
2025-06-25 16:10:48 -07:00
committed by GitHub
parent ad064a8f8a
commit 496a6f8968
17 changed files with 456 additions and 72 deletions

View File

@@ -14,7 +14,10 @@ import (
"context"
"database/sql"
"fmt"
"github.com/rancher/steve/pkg/sqlcache/sqltypes"
"reflect"
"regexp"
"strings"
"testing"
"github.com/rancher/steve/pkg/sqlcache/db"
@@ -22,6 +25,7 @@ import (
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func testStoreKeyFunc(obj interface{}) (string, error) {
@@ -670,6 +674,96 @@ func TestResync(t *testing.T) {
}
}
type StringMatcher struct {
expected string
}
var ptn = regexp.MustCompile(`\s\s+`)
func dropWhiteSpace(s string) string {
s1 := strings.TrimSpace(s)
s2 := strings.ReplaceAll(s1, "\n", " ")
s3 := strings.ReplaceAll(s2, "\r", " ")
return ptn.ReplaceAllString(s3, " ")
}
func (m StringMatcher) Matches(x any) bool {
s, ok := x.(string)
if !ok {
return false
}
return dropWhiteSpace(s) == m.expected
}
func (m StringMatcher) String() string {
return m.expected
}
func WSIgnoringMatcher(expected string) gomock.Matcher {
return StringMatcher{
expected: dropWhiteSpace(expected),
}
}
func TestAddWithExternalUpdates(t *testing.T) {
type testCase struct {
description string
test func(t *testing.T)
}
testObject := testStoreObject{Id: "testStoreObject", Val: "a"}
var tests []testCase
tests = append(tests, testCase{description: "Add with no DB client errors", test: func(t *testing.T) {
c, txC := SetupMockDB(t)
stmts := NewMockStmt(gomock.NewController(t))
store := SetupStoreWithExternalDependencies(t, c)
c.EXPECT().Upsert(txC, store.upsertStmt, "testStoreObject", testObject, store.shouldEncrypt).Return(nil)
c.EXPECT().WithTransaction(gomock.Any(), true, gomock.Any()).Return(nil).Do(
func(ctx context.Context, shouldEncrypt bool, f db.WithTransactionFunction) {
err := f(txC)
if err != nil {
t.Fail()
}
}).Times(2)
rawStmt := `SELECT DISTINCT f.key, ex2."spec.displayName" FROM "_v1_Namespace_fields" f
LEFT OUTER JOIN "_v1_Namespace_labels" lt1 ON f.key = lt1.key
JOIN "management.cattle.io_v3_Project_fields" ex2 ON lt1.value = ex2."metadata.name"
WHERE lt1.label = ? AND f."spec.displayName" != ex2."spec.displayName"`
c.EXPECT().Prepare(WSIgnoringMatcher(rawStmt))
results1 := []any{"field.cattle.io/projectId"}
c.EXPECT().QueryForRows(gomock.Any(), gomock.Any(), results1)
c.EXPECT().ReadStrings2(gomock.Any()).Return([][]string{{"lego.cattle.io/fields1", "moose1"}}, nil)
rawStmt2 := `UPDATE "_v1_Namespace_fields" SET "spec.displayName" = ? WHERE key = ?`
c.EXPECT().Prepare(rawStmt2)
txC.EXPECT().Stmt(gomock.Any()).Return(stmts)
stmts.EXPECT().Exec("moose1", "lego.cattle.io/fields1")
rawStmt3 := `SELECT f.key, ex2."spec.projectName" FROM "_v1_Pods_fields" f
JOIN "provisioner.cattle.io_v3_Cluster_fields" ex2 ON f."field.cattle.io/fixer" = ex2."metadata.name"
WHERE f."spec.projectName" != ex2."spec.projectName"`
c.EXPECT().Prepare(WSIgnoringMatcher(rawStmt3))
results2 := []any{"field.cattle.io/fixer"}
c.EXPECT().QueryForRows(gomock.Any(), gomock.Any(), results2)
c.EXPECT().ReadStrings2(gomock.Any()).Return([][]string{{"lego.cattle.io/fields2", "moose2"}}, nil)
rawStmt4 := `UPDATE "_v1_Pods_fields" SET "spec.projectName" = ? WHERE key = ?`
c.EXPECT().Prepare(rawStmt4)
txC.EXPECT().Stmt(gomock.Any()).Return(stmts)
stmts.EXPECT().Exec("moose2", "lego.cattle.io/fields2")
err := store.Add(testObject)
assert.Nil(t, err)
},
})
t.Parallel()
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
test.test(t)
})
}
}
func SetupMockDB(t *testing.T) (*MockClient, *MockTXClient) {
dbC := NewMockClient(gomock.NewController(t)) // add functionality once store expectation are known
txC := NewMockTXClient(gomock.NewController(t))
@@ -693,7 +787,42 @@ func SetupMockDB(t *testing.T) (*MockClient, *MockTXClient) {
return dbC, txC
}
func SetupStore(t *testing.T, client *MockClient, shouldEncrypt bool) *Store {
store, err := NewStore(context.Background(), testStoreObject{}, testStoreKeyFunc, client, shouldEncrypt, "testStoreObject")
name := "testStoreObject"
gvk := schema.GroupVersionKind{Group: "", Version: "v1", Kind: name}
store, err := NewStore(context.Background(), testStoreObject{}, testStoreKeyFunc, client, shouldEncrypt, gvk, name, nil)
if err != nil {
t.Error(err)
}
return store
}
func gvkKey(group, version, kind string) string {
return group + "_" + version + "_" + kind
}
func SetupStoreWithExternalDependencies(t *testing.T, client *MockClient) *Store {
name := "testStoreObject"
gvk := schema.GroupVersionKind{Group: "", Version: "v1", Kind: name}
namespaceProjectLabelDep := sqltypes.ExternalLabelDependency{
SourceGVK: gvkKey("", "v1", "Namespace"),
SourceLabelName: "field.cattle.io/projectId",
TargetGVK: gvkKey("management.cattle.io", "v3", "Project"),
TargetKeyFieldName: "metadata.name",
TargetFinalFieldName: "spec.displayName",
}
namespaceNonLabelDep := sqltypes.ExternalDependency{
SourceGVK: gvkKey("", "v1", "Pods"),
SourceFieldName: "field.cattle.io/fixer",
TargetGVK: gvkKey("provisioner.cattle.io", "v3", "Cluster"),
TargetKeyFieldName: "metadata.name",
TargetFinalFieldName: "spec.projectName",
}
updateInfo := sqltypes.ExternalGVKUpdates{
AffectedGVK: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"},
ExternalDependencies: []sqltypes.ExternalDependency{namespaceNonLabelDep},
ExternalLabelDependencies: []sqltypes.ExternalLabelDependency{namespaceProjectLabelDep},
}
store, err := NewStore(context.Background(), testStoreObject{}, testStoreKeyFunc, client, false, gvk, name, &updateInfo)
if err != nil {
t.Error(err)
}