123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- /*
- 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 petset
- import (
- "fmt"
- "time"
- inf "gopkg.in/inf.v0"
- "k8s.io/kubernetes/pkg/api"
- api_pod "k8s.io/kubernetes/pkg/api/pod"
- "k8s.io/kubernetes/pkg/api/resource"
- "k8s.io/kubernetes/pkg/api/unversioned"
- "k8s.io/kubernetes/pkg/apis/apps"
- "k8s.io/kubernetes/pkg/client/record"
- "k8s.io/kubernetes/pkg/types"
- "k8s.io/kubernetes/pkg/util/sets"
- )
- func dec(i int64, exponent int) *inf.Dec {
- return inf.NewDec(i, inf.Scale(-exponent))
- }
- func newPVC(name string) api.PersistentVolumeClaim {
- return api.PersistentVolumeClaim{
- ObjectMeta: api.ObjectMeta{
- Name: name,
- },
- Spec: api.PersistentVolumeClaimSpec{
- Resources: api.ResourceRequirements{
- Requests: api.ResourceList{
- api.ResourceStorage: *resource.NewQuantity(1, resource.BinarySI),
- },
- },
- },
- }
- }
- func newPetSetWithVolumes(replicas int, name string, petMounts []api.VolumeMount, podMounts []api.VolumeMount) *apps.PetSet {
- mounts := append(petMounts, podMounts...)
- claims := []api.PersistentVolumeClaim{}
- for _, m := range petMounts {
- claims = append(claims, newPVC(m.Name))
- }
- vols := []api.Volume{}
- for _, m := range podMounts {
- vols = append(vols, api.Volume{
- Name: m.Name,
- VolumeSource: api.VolumeSource{
- HostPath: &api.HostPathVolumeSource{
- Path: fmt.Sprintf("/tmp/%v", m.Name),
- },
- },
- })
- }
- return &apps.PetSet{
- TypeMeta: unversioned.TypeMeta{
- Kind: "PetSet",
- APIVersion: "apps/v1beta1",
- },
- ObjectMeta: api.ObjectMeta{
- Name: name,
- Namespace: api.NamespaceDefault,
- UID: types.UID("test"),
- },
- Spec: apps.PetSetSpec{
- Selector: &unversioned.LabelSelector{
- MatchLabels: map[string]string{"foo": "bar"},
- },
- Replicas: replicas,
- Template: api.PodTemplateSpec{
- Spec: api.PodSpec{
- Containers: []api.Container{
- {
- Name: "nginx",
- Image: "nginx",
- VolumeMounts: mounts,
- },
- },
- Volumes: vols,
- },
- },
- VolumeClaimTemplates: claims,
- ServiceName: "governingsvc",
- },
- }
- }
- func runningPod(ns, name string) *api.Pod {
- p := &api.Pod{Status: api.PodStatus{Phase: api.PodRunning}}
- p.Namespace = ns
- p.Name = name
- return p
- }
- func newPodList(ps *apps.PetSet, num int) []*api.Pod {
- // knownPods are pods in the system
- knownPods := []*api.Pod{}
- for i := 0; i < num; i++ {
- k, _ := newPCB(fmt.Sprintf("%v", i), ps)
- knownPods = append(knownPods, k.pod)
- }
- return knownPods
- }
- func newPetSet(replicas int) *apps.PetSet {
- petMounts := []api.VolumeMount{
- {Name: "datadir", MountPath: "/tmp/zookeeper"},
- }
- podMounts := []api.VolumeMount{
- {Name: "home", MountPath: "/home"},
- }
- return newPetSetWithVolumes(replicas, "foo", petMounts, podMounts)
- }
- func checkPodForMount(pod *api.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: []api.PersistentVolumeClaim{},
- recorder: &record.FakeRecorder{},
- petHealthChecker: &defaultPetHealthChecker{},
- }
- }
- type fakePetClient struct {
- pets []*pcb
- claims []api.PersistentVolumeClaim
- petsCreated, petsDeleted int
- claimsCreated, 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, api.EventTypeNormal, "SuccessfulDelete", "pet: %v", pet.pod.Name)
- continue
- }
- pets = append(pets, f.pets[i])
- }
- if !found {
- // TODO: Return proper not found error
- return fmt.Errorf("Delete failed: pet %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: pet %v already exists", p.pod.Name)
- }
- }
- f.recorder.Eventf(p.parent, api.EventTypeNormal, "SuccessfulCreate", "pet: %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[api_pod.PodHostnameAnnotation] = wanted.pod.Annotations[api_pod.PodHostnameAnnotation]
- f.pets[i].pod.Annotations[api_pod.PodSubdomainAnnotation] = wanted.pod.Annotations[api_pod.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 pet %v not found", wanted.pod.Name)
- }
- // TODO: Delete pvcs/volumes that are in wanted but not in expected.
- return nil
- }
- func (f *fakePetClient) getPodList() []*api.Pod {
- p := []*api.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 = api.PodRunning
- f.pets[index].pod.Annotations[PetSetInitAnnotation] = "true"
- f.pets[index].pod.Status.Conditions = []api.PodCondition{
- {Type: api.PodReady, Status: api.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 *api.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 = &unversioned.Time{Time: time.Now()}
- return nil
- }
- // SyncPVCs fakes pvc syncing.
- func (f *fakePetClient) SyncPVCs(pet *pcb) error {
- v := pet.pvcs
- updateClaims := map[string]api.PersistentVolumeClaim{}
- for i, update := range v {
- updateClaims[update.Name] = v[i]
- }
- claimList := []api.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, api.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 := []api.PersistentVolumeClaim{}
- for i, existing := range f.claims {
- if deleteClaimNames.Has(existing.Name) {
- deleteClaimNames.Delete(existing.Name)
- f.claimsDeleted++
- f.recorder.Eventf(pet.parent, api.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
- }
|