diff --git a/internal/testutil/fakeclient/tracker.go b/internal/testutil/fakeclient/tracker.go index c319e089cd42..5fe762e3d80b 100644 --- a/internal/testutil/fakeclient/tracker.go +++ b/internal/testutil/fakeclient/tracker.go @@ -20,6 +20,8 @@ import ( "fmt" "net/http" "reflect" + "strconv" + "time" "unsafe" "k8s.io/apimachinery/pkg/api/meta" @@ -128,6 +130,16 @@ func (t *TransformingObjectTracker) List(gvr schema.GroupVersionResource, gvk sc return obj, fmt.Errorf("failed to externalize object: %w", err) } + // Set a fake resource version, so that watches work. + if external, ok := external.(interface { + GetResourceVersion() string + SetResourceVersion(string) + }); ok { + if external.GetResourceVersion() == "" { + external.SetResourceVersion(strconv.FormatInt(time.Now().UnixMilli(), 10)) + } + } + return external, nil } diff --git a/pkg/applier/manager_test.go b/pkg/applier/manager_test.go new file mode 100644 index 000000000000..1df8dd8ec25d --- /dev/null +++ b/pkg/applier/manager_test.go @@ -0,0 +1,98 @@ +/* +Copyright 2024 k0s authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package applier_test + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/k0sproject/k0s/internal/testutil" + "github.com/k0sproject/k0s/pkg/applier" + "github.com/k0sproject/k0s/pkg/component/controller/leaderelector" + "github.com/k0sproject/k0s/pkg/config" + "github.com/k0sproject/k0s/pkg/constant" + "github.com/k0sproject/k0s/pkg/kubernetes/watch" + + corev1 "k8s.io/api/core/v1" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestManager_AppliesStacks(t *testing.T) { + k0sVars, err := config.NewCfgVars(nil, t.TempDir()) + require.NoError(t, err) + leaderElector := leaderelector.Dummy{Leader: true} + clients := testutil.NewFakeClientFactory() + + underTest := applier.Manager{ + K0sVars: k0sVars, + KubeClientFactory: clients, + LeaderElector: &leaderElector, + } + + // A stack that exists on disk before the manager is started. + before := filepath.Join(k0sVars.ManifestsDir, "before") + require.NoError(t, os.MkdirAll(before, constant.ManifestsDirMode)) + os.WriteFile(filepath.Join(before, "before.yaml"), []byte(` +apiVersion: v1 +kind: ConfigMap +metadata: + name: before + namespace: default + resourceVersion: "1" +data: {} +`, + ), constant.CertMode) + + require.NoError(t, leaderElector.Init(context.TODO())) + require.NoError(t, underTest.Init(context.TODO())) + require.NoError(t, underTest.Start(context.TODO())) + t.Cleanup(func() { assert.NoError(t, underTest.Stop()) }) + require.NoError(t, leaderElector.Start(context.TODO())) + t.Cleanup(func() { assert.NoError(t, leaderElector.Stop()) }) + + // Wait for the "before" stack to be applied. + require.NoError(t, watch.ConfigMaps(clients.Client.CoreV1().ConfigMaps("default")). + Until(context.TODO(), func(item *corev1.ConfigMap) (bool, error) { + return item.Name == "before", nil + }), + ) + + // A stack that is created on disk after the manager has started. + after := filepath.Join(k0sVars.ManifestsDir, "after") + require.NoError(t, os.MkdirAll(after, constant.ManifestsDirMode)) + os.WriteFile(filepath.Join(after, "after.yaml"), []byte(` +apiVersion: v1 +kind: ConfigMap +metadata: + name: after + namespace: default + resourceVersion: "1" +data: {} +`, + ), constant.CertMode) + + // Wait for the "after" stack to be applied. + require.NoError(t, watch.ConfigMaps(clients.Client.CoreV1().ConfigMaps("default")). + Until(context.TODO(), func(item *corev1.ConfigMap) (bool, error) { + return item.Name == "after", nil + }), + ) +}