mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			328 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			328 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2016 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 statefulset
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"time"
 | 
						|
 | 
						|
	inf "gopkg.in/inf.v0"
 | 
						|
 | 
						|
	"k8s.io/apimachinery/pkg/api/resource"
 | 
						|
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						|
	"k8s.io/apimachinery/pkg/types"
 | 
						|
	"k8s.io/apimachinery/pkg/util/sets"
 | 
						|
	"k8s.io/kubernetes/pkg/api/v1"
 | 
						|
	apipod "k8s.io/kubernetes/pkg/api/v1/pod"
 | 
						|
	apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
 | 
						|
	"k8s.io/kubernetes/pkg/client/record"
 | 
						|
)
 | 
						|
 | 
						|
func dec(i int64, exponent int) *inf.Dec {
 | 
						|
	return inf.NewDec(i, inf.Scale(-exponent))
 | 
						|
}
 | 
						|
 | 
						|
func newPVC(name string) v1.PersistentVolumeClaim {
 | 
						|
	return v1.PersistentVolumeClaim{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{
 | 
						|
			Name: name,
 | 
						|
		},
 | 
						|
		Spec: v1.PersistentVolumeClaimSpec{
 | 
						|
			Resources: v1.ResourceRequirements{
 | 
						|
				Requests: v1.ResourceList{
 | 
						|
					v1.ResourceStorage: *resource.NewQuantity(1, resource.BinarySI),
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func newStatefulSetWithVolumes(replicas int, name string, petMounts []v1.VolumeMount, podMounts []v1.VolumeMount) *apps.StatefulSet {
 | 
						|
	mounts := append(petMounts, podMounts...)
 | 
						|
	claims := []v1.PersistentVolumeClaim{}
 | 
						|
	for _, m := range petMounts {
 | 
						|
		claims = append(claims, newPVC(m.Name))
 | 
						|
	}
 | 
						|
 | 
						|
	vols := []v1.Volume{}
 | 
						|
	for _, m := range podMounts {
 | 
						|
		vols = append(vols, v1.Volume{
 | 
						|
			Name: m.Name,
 | 
						|
			VolumeSource: v1.VolumeSource{
 | 
						|
				HostPath: &v1.HostPathVolumeSource{
 | 
						|
					Path: fmt.Sprintf("/tmp/%v", m.Name),
 | 
						|
				},
 | 
						|
			},
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	return &apps.StatefulSet{
 | 
						|
		TypeMeta: metav1.TypeMeta{
 | 
						|
			Kind:       "StatefulSet",
 | 
						|
			APIVersion: "apps/v1beta1",
 | 
						|
		},
 | 
						|
		ObjectMeta: metav1.ObjectMeta{
 | 
						|
			Name:      name,
 | 
						|
			Namespace: metav1.NamespaceDefault,
 | 
						|
			UID:       types.UID("test"),
 | 
						|
		},
 | 
						|
		Spec: apps.StatefulSetSpec{
 | 
						|
			Selector: &metav1.LabelSelector{
 | 
						|
				MatchLabels: map[string]string{"foo": "bar"},
 | 
						|
			},
 | 
						|
			Replicas: func() *int32 { i := int32(replicas); return &i }(),
 | 
						|
			Template: v1.PodTemplateSpec{
 | 
						|
				Spec: v1.PodSpec{
 | 
						|
					Containers: []v1.Container{
 | 
						|
						{
 | 
						|
							Name:         "nginx",
 | 
						|
							Image:        "nginx",
 | 
						|
							VolumeMounts: mounts,
 | 
						|
						},
 | 
						|
					},
 | 
						|
					Volumes: vols,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			VolumeClaimTemplates: claims,
 | 
						|
			ServiceName:          "governingsvc",
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func runningPod(ns, name string) *v1.Pod {
 | 
						|
	p := &v1.Pod{Status: v1.PodStatus{Phase: v1.PodRunning}}
 | 
						|
	p.Namespace = ns
 | 
						|
	p.Name = name
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func newPodList(ps *apps.StatefulSet, num int) []*v1.Pod {
 | 
						|
	// knownPods are pods in the system
 | 
						|
	knownPods := []*v1.Pod{}
 | 
						|
	for i := 0; i < num; i++ {
 | 
						|
		k, _ := newPCB(fmt.Sprintf("%v", i), ps)
 | 
						|
		knownPods = append(knownPods, k.pod)
 | 
						|
	}
 | 
						|
	return knownPods
 | 
						|
}
 | 
						|
 | 
						|
func newStatefulSet(replicas int) *apps.StatefulSet {
 | 
						|
	petMounts := []v1.VolumeMount{
 | 
						|
		{Name: "datadir", MountPath: "/tmp/zookeeper"},
 | 
						|
	}
 | 
						|
	podMounts := []v1.VolumeMount{
 | 
						|
		{Name: "home", MountPath: "/home"},
 | 
						|
	}
 | 
						|
	return newStatefulSetWithVolumes(replicas, "foo", petMounts, podMounts)
 | 
						|
}
 | 
						|
 | 
						|
func checkPodForMount(pod *v1.Pod, mountName string) error {
 | 
						|
	for _, c := range pod.Spec.Containers {
 | 
						|
		for _, v := range c.VolumeMounts {
 | 
						|
			if v.Name == mountName {
 | 
						|
				return nil
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return fmt.Errorf("Found volume but no associated mount %v in pod %v", mountName, pod.Name)
 | 
						|
}
 | 
						|
 | 
						|
func newFakePetClient() *fakePetClient {
 | 
						|
	return &fakePetClient{
 | 
						|
		pets:             []*pcb{},
 | 
						|
		claims:           []v1.PersistentVolumeClaim{},
 | 
						|
		recorder:         &record.FakeRecorder{},
 | 
						|
		petHealthChecker: &defaultPetHealthChecker{},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type fakePetClient struct {
 | 
						|
	pets          []*pcb
 | 
						|
	claims        []v1.PersistentVolumeClaim
 | 
						|
	petsCreated   int
 | 
						|
	petsDeleted   int
 | 
						|
	claimsCreated int
 | 
						|
	claimsDeleted int
 | 
						|
	recorder      record.EventRecorder
 | 
						|
	petHealthChecker
 | 
						|
}
 | 
						|
 | 
						|
// Delete fakes pet client deletion.
 | 
						|
func (f *fakePetClient) Delete(p *pcb) error {
 | 
						|
	pets := []*pcb{}
 | 
						|
	found := false
 | 
						|
	for i, pet := range f.pets {
 | 
						|
		if p.pod.Name == pet.pod.Name {
 | 
						|
			found = true
 | 
						|
			f.recorder.Eventf(pet.parent, v1.EventTypeNormal, "SuccessfulDelete", "pod: %v", pet.pod.Name)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		pets = append(pets, f.pets[i])
 | 
						|
	}
 | 
						|
	if !found {
 | 
						|
		// TODO: Return proper not found error
 | 
						|
		return fmt.Errorf("Delete failed: pod %v doesn't exist", p.pod.Name)
 | 
						|
	}
 | 
						|
	f.pets = pets
 | 
						|
	f.petsDeleted++
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Get fakes getting pets.
 | 
						|
func (f *fakePetClient) Get(p *pcb) (*pcb, bool, error) {
 | 
						|
	for i, pet := range f.pets {
 | 
						|
		if p.pod.Name == pet.pod.Name {
 | 
						|
			return f.pets[i], true, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil, false, nil
 | 
						|
}
 | 
						|
 | 
						|
// Create fakes pet creation.
 | 
						|
func (f *fakePetClient) Create(p *pcb) error {
 | 
						|
	for _, pet := range f.pets {
 | 
						|
		if p.pod.Name == pet.pod.Name {
 | 
						|
			return fmt.Errorf("Create failed: pod %v already exists", p.pod.Name)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	f.recorder.Eventf(p.parent, v1.EventTypeNormal, "SuccessfulCreate", "pod: %v", p.pod.Name)
 | 
						|
	f.pets = append(f.pets, p)
 | 
						|
	f.petsCreated++
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Update fakes pet updates.
 | 
						|
func (f *fakePetClient) Update(expected, wanted *pcb) error {
 | 
						|
	found := false
 | 
						|
	pets := []*pcb{}
 | 
						|
	for i, pet := range f.pets {
 | 
						|
		if wanted.pod.Name == pet.pod.Name {
 | 
						|
			f.pets[i].pod.Annotations[apipod.PodHostnameAnnotation] = wanted.pod.Annotations[apipod.PodHostnameAnnotation]
 | 
						|
			f.pets[i].pod.Annotations[apipod.PodSubdomainAnnotation] = wanted.pod.Annotations[apipod.PodSubdomainAnnotation]
 | 
						|
			f.pets[i].pod.Spec = wanted.pod.Spec
 | 
						|
			found = true
 | 
						|
		}
 | 
						|
		pets = append(pets, f.pets[i])
 | 
						|
	}
 | 
						|
	f.pets = pets
 | 
						|
	if !found {
 | 
						|
		return fmt.Errorf("Cannot update pod %v not found", wanted.pod.Name)
 | 
						|
	}
 | 
						|
	// TODO: Delete pvcs/volumes that are in wanted but not in expected.
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *fakePetClient) getPodList() []*v1.Pod {
 | 
						|
	p := []*v1.Pod{}
 | 
						|
	for i, pet := range f.pets {
 | 
						|
		if pet.pod == nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		p = append(p, f.pets[i].pod)
 | 
						|
	}
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func (f *fakePetClient) deletePetAtIndex(index int) {
 | 
						|
	p := []*pcb{}
 | 
						|
	for i := range f.pets {
 | 
						|
		if i != index {
 | 
						|
			p = append(p, f.pets[i])
 | 
						|
		}
 | 
						|
	}
 | 
						|
	f.pets = p
 | 
						|
}
 | 
						|
 | 
						|
func (f *fakePetClient) setHealthy(index int) error {
 | 
						|
	if len(f.pets) <= index {
 | 
						|
		return fmt.Errorf("Index out of range, len %v index %v", len(f.pets), index)
 | 
						|
	}
 | 
						|
	f.pets[index].pod.Status.Phase = v1.PodRunning
 | 
						|
	f.pets[index].pod.Annotations[StatefulSetInitAnnotation] = "true"
 | 
						|
	f.pets[index].pod.Status.Conditions = []v1.PodCondition{
 | 
						|
		{Type: v1.PodReady, Status: v1.ConditionTrue},
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// isHealthy is a convenience wrapper around the default health checker.
 | 
						|
// The first invocation returns not-healthy, but marks the pet healthy so
 | 
						|
// subsequent invocations see it as healthy.
 | 
						|
func (f *fakePetClient) isHealthy(pod *v1.Pod) bool {
 | 
						|
	if f.petHealthChecker.isHealthy(pod) {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func (f *fakePetClient) setDeletionTimestamp(index int) error {
 | 
						|
	if len(f.pets) <= index {
 | 
						|
		return fmt.Errorf("Index out of range, len %v index %v", len(f.pets), index)
 | 
						|
	}
 | 
						|
	f.pets[index].pod.DeletionTimestamp = &metav1.Time{Time: time.Now()}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// SyncPVCs fakes pvc syncing.
 | 
						|
func (f *fakePetClient) SyncPVCs(pet *pcb) error {
 | 
						|
	v := pet.pvcs
 | 
						|
	updateClaims := map[string]v1.PersistentVolumeClaim{}
 | 
						|
	for i, update := range v {
 | 
						|
		updateClaims[update.Name] = v[i]
 | 
						|
	}
 | 
						|
	claimList := []v1.PersistentVolumeClaim{}
 | 
						|
	for i, existing := range f.claims {
 | 
						|
		if update, ok := updateClaims[existing.Name]; ok {
 | 
						|
			claimList = append(claimList, update)
 | 
						|
			delete(updateClaims, existing.Name)
 | 
						|
		} else {
 | 
						|
			claimList = append(claimList, f.claims[i])
 | 
						|
		}
 | 
						|
	}
 | 
						|
	for _, remaining := range updateClaims {
 | 
						|
		claimList = append(claimList, remaining)
 | 
						|
		f.claimsCreated++
 | 
						|
		f.recorder.Eventf(pet.parent, v1.EventTypeNormal, "SuccessfulCreate", "pvc: %v", remaining.Name)
 | 
						|
	}
 | 
						|
	f.claims = claimList
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// DeletePVCs fakes pvc deletion.
 | 
						|
func (f *fakePetClient) DeletePVCs(pet *pcb) error {
 | 
						|
	claimsToDelete := pet.pvcs
 | 
						|
	deleteClaimNames := sets.NewString()
 | 
						|
	for _, c := range claimsToDelete {
 | 
						|
		deleteClaimNames.Insert(c.Name)
 | 
						|
	}
 | 
						|
	pvcs := []v1.PersistentVolumeClaim{}
 | 
						|
	for i, existing := range f.claims {
 | 
						|
		if deleteClaimNames.Has(existing.Name) {
 | 
						|
			deleteClaimNames.Delete(existing.Name)
 | 
						|
			f.claimsDeleted++
 | 
						|
			f.recorder.Eventf(pet.parent, v1.EventTypeNormal, "SuccessfulDelete", "pvc: %v", existing.Name)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		pvcs = append(pvcs, f.claims[i])
 | 
						|
	}
 | 
						|
	f.claims = pvcs
 | 
						|
	if deleteClaimNames.Len() != 0 {
 | 
						|
		return fmt.Errorf("Claims %+v don't exist. Failed deletion.", deleteClaimNames)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 |