mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-11-02 23:02:25 +00:00
255 lines
8.6 KiB
Go
255 lines
8.6 KiB
Go
/*
|
|
Copyright 2017 The Kubernetes 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 daemonset
|
|
|
|
import (
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"k8s.io/api/core/v1"
|
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
|
"k8s.io/client-go/informers"
|
|
clientset "k8s.io/client-go/kubernetes"
|
|
corev1typed "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
restclient "k8s.io/client-go/rest"
|
|
"k8s.io/kubernetes/pkg/controller/daemon"
|
|
"k8s.io/kubernetes/pkg/util/metrics"
|
|
"k8s.io/kubernetes/test/integration/framework"
|
|
)
|
|
|
|
func setup(t *testing.T) (*httptest.Server, framework.CloseFunc, *daemon.DaemonSetsController, informers.SharedInformerFactory, clientset.Interface) {
|
|
masterConfig := framework.NewIntegrationTestMasterConfig()
|
|
_, server, closeFn := framework.RunAMaster(masterConfig)
|
|
|
|
config := restclient.Config{Host: server.URL}
|
|
clientSet, err := clientset.NewForConfig(&config)
|
|
if err != nil {
|
|
t.Fatalf("error in creating clientset: %v", err)
|
|
}
|
|
resyncPeriod := 12 * time.Hour
|
|
dsInformers := informers.NewSharedInformerFactory(clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "daemonset-informers")), resyncPeriod)
|
|
metrics.UnregisterMetricAndUntrackRateLimiterUsage("daemon_controller")
|
|
dc, err := daemon.NewDaemonSetsController(
|
|
dsInformers.Apps().V1().DaemonSets(),
|
|
dsInformers.Apps().V1().ControllerRevisions(),
|
|
dsInformers.Core().V1().Pods(),
|
|
dsInformers.Core().V1().Nodes(),
|
|
clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "daemonset-controller")),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("error creating DaemonSets controller: %v", err)
|
|
}
|
|
|
|
return server, closeFn, dc, dsInformers, clientSet
|
|
}
|
|
|
|
func TestOneNodeDaemonLaunchesPod(t *testing.T) {
|
|
for _, strategy := range updateStrategies() {
|
|
server, closeFn, dc, informers, clientset := setup(t)
|
|
defer closeFn()
|
|
ns := framework.CreateTestingNamespace("one-node-daemonset-test", server, t)
|
|
defer framework.DeleteTestingNamespace(ns, server, t)
|
|
|
|
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
|
podClient := clientset.CoreV1().Pods(ns.Name)
|
|
nodeClient := clientset.CoreV1().Nodes()
|
|
podInformer := informers.Core().V1().Pods().Informer()
|
|
stopCh := make(chan struct{})
|
|
informers.Start(stopCh)
|
|
go dc.Run(5, stopCh)
|
|
|
|
ds := newDaemonSet("foo", ns.Name)
|
|
ds.Spec.UpdateStrategy = *strategy
|
|
_, err := dsClient.Create(ds)
|
|
if err != nil {
|
|
t.Fatalf("failed to create DaemonSet: %v", err)
|
|
}
|
|
_, err = nodeClient.Create(newNode("single-node", nil))
|
|
if err != nil {
|
|
t.Fatalf("failed to create node: %v", err)
|
|
}
|
|
|
|
validateDaemonSetPodsAndMarkReady(podClient, podInformer, 1, t)
|
|
validateDaemonSetStatus(dsClient, ds.Name, ds.Namespace, 1, t)
|
|
|
|
close(stopCh)
|
|
}
|
|
}
|
|
|
|
func TestSimpleDaemonSetLaunchesPods(t *testing.T) {
|
|
for _, strategy := range updateStrategies() {
|
|
server, closeFn, dc, informers, clientset := setup(t)
|
|
defer closeFn()
|
|
ns := framework.CreateTestingNamespace("simple-daemonset-test", server, t)
|
|
defer framework.DeleteTestingNamespace(ns, server, t)
|
|
|
|
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
|
podClient := clientset.CoreV1().Pods(ns.Name)
|
|
nodeClient := clientset.CoreV1().Nodes()
|
|
podInformer := informers.Core().V1().Pods().Informer()
|
|
stopCh := make(chan struct{})
|
|
informers.Start(stopCh)
|
|
go dc.Run(5, stopCh)
|
|
|
|
ds := newDaemonSet("foo", ns.Name)
|
|
ds.Spec.UpdateStrategy = *strategy
|
|
_, err := dsClient.Create(ds)
|
|
if err != nil {
|
|
t.Fatalf("failed to create DaemonSet: %v", err)
|
|
}
|
|
addNodes(nodeClient, 0, 5, nil, t)
|
|
|
|
validateDaemonSetPodsAndMarkReady(podClient, podInformer, 5, t)
|
|
validateDaemonSetStatus(dsClient, ds.Name, ds.Namespace, 5, t)
|
|
|
|
close(stopCh)
|
|
}
|
|
}
|
|
|
|
func TestNotReadyNodeDaemonDoesLaunchPod(t *testing.T) {
|
|
for _, strategy := range updateStrategies() {
|
|
server, closeFn, dc, informers, clientset := setup(t)
|
|
defer closeFn()
|
|
ns := framework.CreateTestingNamespace("simple-daemonset-test", server, t)
|
|
defer framework.DeleteTestingNamespace(ns, server, t)
|
|
|
|
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
|
podClient := clientset.CoreV1().Pods(ns.Name)
|
|
nodeClient := clientset.CoreV1().Nodes()
|
|
podInformer := informers.Core().V1().Pods().Informer()
|
|
stopCh := make(chan struct{})
|
|
informers.Start(stopCh)
|
|
go dc.Run(5, stopCh)
|
|
|
|
ds := newDaemonSet("foo", ns.Name)
|
|
ds.Spec.UpdateStrategy = *strategy
|
|
_, err := dsClient.Create(ds)
|
|
if err != nil {
|
|
t.Fatalf("failed to create DaemonSet: %v", err)
|
|
}
|
|
node := newNode("single-node", nil)
|
|
node.Status.Conditions = []v1.NodeCondition{
|
|
{Type: v1.NodeReady, Status: v1.ConditionFalse},
|
|
}
|
|
_, err = nodeClient.Create(node)
|
|
if err != nil {
|
|
t.Fatalf("failed to create node: %v", err)
|
|
}
|
|
|
|
validateDaemonSetPodsAndMarkReady(podClient, podInformer, 1, t)
|
|
validateDaemonSetStatus(dsClient, ds.Name, ds.Namespace, 1, t)
|
|
|
|
close(stopCh)
|
|
}
|
|
}
|
|
|
|
func TestInsufficientCapacityNodeDaemonDoesNotLaunchPod(t *testing.T) {
|
|
for _, strategy := range updateStrategies() {
|
|
server, closeFn, dc, informers, clientset := setup(t)
|
|
defer closeFn()
|
|
ns := framework.CreateTestingNamespace("insufficient-capacity", server, t)
|
|
defer framework.DeleteTestingNamespace(ns, server, t)
|
|
|
|
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
|
nodeClient := clientset.CoreV1().Nodes()
|
|
eventClient := corev1typed.New(clientset.CoreV1().RESTClient()).Events(ns.Namespace)
|
|
stopCh := make(chan struct{})
|
|
informers.Start(stopCh)
|
|
go dc.Run(5, stopCh)
|
|
|
|
ds := newDaemonSet("foo", ns.Name)
|
|
ds.Spec.Template.Spec = resourcePodSpec("node-with-limited-memory", "120M", "75m")
|
|
ds.Spec.UpdateStrategy = *strategy
|
|
_, err := dsClient.Create(ds)
|
|
if err != nil {
|
|
t.Fatalf("failed to create DaemonSet: %v", err)
|
|
}
|
|
node := newNode("node-with-limited-memory", nil)
|
|
node.Status.Allocatable = allocatableResources("100M", "200m")
|
|
_, err = nodeClient.Create(node)
|
|
if err != nil {
|
|
t.Fatalf("failed to create node: %v", err)
|
|
}
|
|
|
|
validateFailedPlacementEvent(eventClient, t)
|
|
|
|
close(stopCh)
|
|
}
|
|
}
|
|
|
|
// A RollingUpdate DaemonSet should adopt existing pods when it is created
|
|
func TestRollingUpdateDaemonSetExistingPodAdoption(t *testing.T) {
|
|
server, closeFn, dc, informers, clientset := setup(t)
|
|
defer closeFn()
|
|
|
|
tearDownFn := setupGC(t, server)
|
|
defer tearDownFn()
|
|
|
|
ns := framework.CreateTestingNamespace("rolling-update-daemonset-existing-pod-adoption-test", server, t)
|
|
defer framework.DeleteTestingNamespace(ns, server, t)
|
|
|
|
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
|
podClient := clientset.CoreV1().Pods(ns.Name)
|
|
nodeClient := clientset.CoreV1().Nodes()
|
|
podInformer := informers.Core().V1().Pods().Informer()
|
|
controllerRevisionClient := clientset.AppsV1().ControllerRevisions(ns.Name)
|
|
stopCh := make(chan struct{})
|
|
defer close(stopCh)
|
|
informers.Start(stopCh)
|
|
go dc.Run(5, stopCh)
|
|
|
|
// Step 1: create a RollingUpdate DaemonSet
|
|
dsName := "daemonset"
|
|
ds := newDaemonSet(dsName, ns.Name)
|
|
ds.Spec.UpdateStrategy = *newRollbackStrategy()
|
|
ds, err := dsClient.Create(ds)
|
|
if err != nil {
|
|
t.Fatalf("failed to create daemonset %q: %v", dsName, err)
|
|
}
|
|
|
|
nodeName := "single-node"
|
|
node := newNode(nodeName, nil)
|
|
_, err = nodeClient.Create(node)
|
|
if err != nil {
|
|
t.Fatalf("failed to create node %q: %v", nodeName, err)
|
|
}
|
|
|
|
// Validate everything works correctly (include marking daemon pod as ready)
|
|
validateDaemonSetPodsAndMarkReady(podClient, podInformer, 1, t)
|
|
validateDaemonSetStatus(dsClient, ds.Name, ds.Namespace, 1, t)
|
|
|
|
// Step 2: delete daemonset and orphan its pods
|
|
deleteDaemonSetAndOrphanPods(dsClient, podClient, controllerRevisionClient, podInformer, ds, t)
|
|
|
|
// Step 3: create 2rd daemonset to adopt the pods (no restart) as long as template matches
|
|
dsName2 := "daemonset-adopt-template-matches"
|
|
ds2 := newDaemonSet(dsName2, ns.Name)
|
|
ds2.Spec.UpdateStrategy = *newRollbackStrategy()
|
|
ds2, err = dsClient.Create(ds2)
|
|
if err != nil {
|
|
t.Fatalf("failed to create daemonset %q: %v", dsName2, err)
|
|
}
|
|
if !apiequality.Semantic.DeepEqual(ds2.Spec.Template, ds.Spec.Template) {
|
|
t.Fatalf(".spec.template of new daemonset %q and old daemonset %q are not the same", dsName2, dsName)
|
|
}
|
|
|
|
// Wait for pods and history to be adopted by 2nd daemonset
|
|
waitDaemonSetAdoption(podClient, controllerRevisionClient, podInformer, ds2, ds.Name, t)
|
|
validateDaemonSetStatus(dsClient, ds2.Name, ds2.Namespace, 1, t)
|
|
}
|