forked from LaconicNetwork/kompose
update vendored dependencies
This commit is contained in:
+136
@@ -0,0 +1,136 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
kerrors "k8s.io/kubernetes/pkg/api/errors"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
kutil "k8s.io/kubernetes/pkg/util"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
"github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
// NewDeploymentConfigReaper returns a new reaper for deploymentConfigs
|
||||
func NewDeploymentConfigReaper(oc client.Interface, kc kclient.Interface) kubectl.Reaper {
|
||||
return &DeploymentConfigReaper{oc: oc, kc: kc, pollInterval: kubectl.Interval, timeout: kubectl.Timeout}
|
||||
}
|
||||
|
||||
// DeploymentConfigReaper implements the Reaper interface for deploymentConfigs
|
||||
type DeploymentConfigReaper struct {
|
||||
oc client.Interface
|
||||
kc kclient.Interface
|
||||
pollInterval, timeout time.Duration
|
||||
}
|
||||
|
||||
// pause marks the deployment configuration as paused to avoid triggering new
|
||||
// deployments.
|
||||
func (reaper *DeploymentConfigReaper) pause(namespace, name string) (*deployapi.DeploymentConfig, error) {
|
||||
return client.UpdateConfigWithRetries(reaper.oc, namespace, name, func(d *deployapi.DeploymentConfig) {
|
||||
d.Spec.RevisionHistoryLimit = kutil.Int32Ptr(0)
|
||||
d.Spec.Replicas = 0
|
||||
d.Spec.Paused = true
|
||||
})
|
||||
}
|
||||
|
||||
// Stop scales a replication controller via its deployment configuration down to
|
||||
// zero replicas, waits for all of them to get deleted and then deletes both the
|
||||
// replication controller and its deployment configuration.
|
||||
func (reaper *DeploymentConfigReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *kapi.DeleteOptions) error {
|
||||
// Pause the deployment configuration to prevent the new deployments from
|
||||
// being triggered.
|
||||
config, err := reaper.pause(namespace, name)
|
||||
configNotFound := kerrors.IsNotFound(err)
|
||||
if err != nil && !configNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
isPaused bool
|
||||
legacy bool
|
||||
)
|
||||
// Determine if the deployment config controller noticed the pause.
|
||||
if !configNotFound {
|
||||
if err := wait.Poll(1*time.Second, 1*time.Minute, func() (bool, error) {
|
||||
dc, err := reaper.oc.DeploymentConfigs(namespace).Get(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
isPaused = dc.Spec.Paused
|
||||
return dc.Status.ObservedGeneration >= config.Generation, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we failed to pause the deployment config, it means we are talking to
|
||||
// old API that does not support pausing. In that case, we delete the
|
||||
// deployment config to stay backward compatible.
|
||||
if !isPaused {
|
||||
if err := reaper.oc.DeploymentConfigs(namespace).Delete(name); err != nil {
|
||||
return err
|
||||
}
|
||||
// Setting this to true avoid deleting the config at the end.
|
||||
legacy = true
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up deployments related to the config. Even if the deployment
|
||||
// configuration has been deleted, we want to sweep the existing replication
|
||||
// controllers and clean them up.
|
||||
options := kapi.ListOptions{LabelSelector: util.ConfigSelector(name)}
|
||||
rcList, err := reaper.kc.ReplicationControllers(namespace).List(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rcReaper, err := kubectl.ReaperFor(kapi.Kind("ReplicationController"), reaper.kc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If there is neither a config nor any deployments, nor any deployer pods, we can return NotFound.
|
||||
deployments := rcList.Items
|
||||
|
||||
if configNotFound && len(deployments) == 0 {
|
||||
return kerrors.NewNotFound(kapi.Resource("deploymentconfig"), name)
|
||||
}
|
||||
|
||||
for _, rc := range deployments {
|
||||
if err = rcReaper.Stop(rc.Namespace, rc.Name, timeout, gracePeriod); err != nil {
|
||||
// Better not error out here...
|
||||
glog.Infof("Cannot delete ReplicationController %s/%s for deployment config %s/%s: %v", rc.Namespace, rc.Name, namespace, name, err)
|
||||
}
|
||||
|
||||
// Only remove deployer pods when the deployment was failed. For completed
|
||||
// deployment the pods should be already deleted.
|
||||
if !util.IsFailedDeployment(&rc) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Delete all deployer and hook pods
|
||||
options = kapi.ListOptions{LabelSelector: util.DeployerPodSelector(rc.Name)}
|
||||
podList, err := reaper.kc.Pods(rc.Namespace).List(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pod := range podList.Items {
|
||||
err := reaper.kc.Pods(pod.Namespace).Delete(pod.Name, gracePeriod)
|
||||
if err != nil {
|
||||
// Better not error out here...
|
||||
glog.Infof("Cannot delete lifecycle Pod %s/%s for deployment config %s/%s: %v", pod.Namespace, pod.Name, namespace, name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing to delete or we already deleted the deployment config because we
|
||||
// failed to pause.
|
||||
if configNotFound || legacy {
|
||||
return nil
|
||||
}
|
||||
|
||||
return reaper.oc.DeploymentConfigs(namespace).Delete(name)
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// Package cmd contains various interface implementations for command-line tools
|
||||
// associated with deploymentconfigs.
|
||||
package cmd
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
)
|
||||
|
||||
var basic = kubectl.BasicReplicationController{}
|
||||
|
||||
type BasicDeploymentConfigController struct{}
|
||||
|
||||
func (BasicDeploymentConfigController) ParamNames() []kubectl.GeneratorParam {
|
||||
return basic.ParamNames()
|
||||
}
|
||||
|
||||
func (BasicDeploymentConfigController) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
obj, err := basic.Generate(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t := obj.(type) {
|
||||
case *kapi.ReplicationController:
|
||||
obj = &deployapi.DeploymentConfig{
|
||||
ObjectMeta: t.ObjectMeta,
|
||||
Spec: deployapi.DeploymentConfigSpec{
|
||||
Selector: t.Spec.Selector,
|
||||
Replicas: t.Spec.Replicas,
|
||||
Template: t.Spec.Template,
|
||||
},
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized object type: %v", reflect.TypeOf(t))
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"text/tabwriter"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
deployutil "github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
func NewDeploymentConfigHistoryViewer(oc client.Interface, kc kclient.Interface) kubectl.HistoryViewer {
|
||||
return &DeploymentConfigHistoryViewer{dn: oc, rn: kc}
|
||||
}
|
||||
|
||||
// DeploymentConfigHistoryViewer is an implementation of the kubectl HistoryViewer interface
|
||||
// for deployment configs.
|
||||
type DeploymentConfigHistoryViewer struct {
|
||||
rn kclient.ReplicationControllersNamespacer
|
||||
dn client.DeploymentConfigsNamespacer
|
||||
}
|
||||
|
||||
var _ kubectl.HistoryViewer = &DeploymentConfigHistoryViewer{}
|
||||
|
||||
// ViewHistory returns a description of all the history it can find for a deployment config.
|
||||
func (h *DeploymentConfigHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
||||
opts := kapi.ListOptions{LabelSelector: deployutil.ConfigSelector(name)}
|
||||
deploymentList, err := h.rn.ReplicationControllers(namespace).List(opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
history := deploymentList.Items
|
||||
|
||||
if len(deploymentList.Items) == 0 {
|
||||
return "No rollout history found.", nil
|
||||
}
|
||||
|
||||
// Print details of a specific revision
|
||||
if revision > 0 {
|
||||
var desired *kapi.PodTemplateSpec
|
||||
// We could use a binary search here but brute-force is always faster to write
|
||||
for i := range history {
|
||||
rc := history[i]
|
||||
|
||||
if deployutil.DeploymentVersionFor(&rc) == revision {
|
||||
desired = rc.Spec.Template
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if desired == nil {
|
||||
return "", fmt.Errorf("unable to find the specified revision")
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
kubectl.DescribePodTemplate(desired, buf)
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
sort.Sort(deployutil.ByLatestVersionAsc(history))
|
||||
|
||||
return tabbedString(func(out *tabwriter.Writer) error {
|
||||
fmt.Fprintf(out, "REVISION\tSTATUS\tCAUSE\n")
|
||||
for i := range history {
|
||||
rc := history[i]
|
||||
|
||||
rev := deployutil.DeploymentVersionFor(&rc)
|
||||
status := deployutil.DeploymentStatusFor(&rc)
|
||||
cause := rc.Annotations[deployapi.DeploymentStatusReasonAnnotation]
|
||||
if len(cause) == 0 {
|
||||
cause = "<unknown>"
|
||||
}
|
||||
fmt.Fprintf(out, "%d\t%s\t%s\n", rev, status, cause)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Re-use from an utility package
|
||||
func tabbedString(f func(*tabwriter.Writer) error) (string, error) {
|
||||
out := new(tabwriter.Writer)
|
||||
buf := &bytes.Buffer{}
|
||||
out.Init(buf, 0, 8, 1, '\t', 0)
|
||||
|
||||
err := f(out)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
out.Flush()
|
||||
str := string(buf.String())
|
||||
return str, nil
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
)
|
||||
|
||||
func NewDeploymentConfigRollbacker(oc client.Interface) kubectl.Rollbacker {
|
||||
return &DeploymentConfigRollbacker{dn: oc}
|
||||
}
|
||||
|
||||
// DeploymentConfigRollbacker is an implementation of the kubectl Rollbacker interface
|
||||
// for deployment configs.
|
||||
type DeploymentConfigRollbacker struct {
|
||||
dn client.DeploymentConfigsNamespacer
|
||||
}
|
||||
|
||||
var _ kubectl.Rollbacker = &DeploymentConfigRollbacker{}
|
||||
|
||||
// Rollback the provided deployment config to a specific revision. If revision is zero, we will
|
||||
// rollback to the previous deployment.
|
||||
func (r *DeploymentConfigRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64) (string, error) {
|
||||
config, ok := obj.(*deployapi.DeploymentConfig)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("passed object is not a deployment config: %#v", obj)
|
||||
}
|
||||
if config.Spec.Paused {
|
||||
return "", fmt.Errorf("cannot rollback a paused config; resume it first with 'rollout resume dc/%s' and try again", config.Name)
|
||||
}
|
||||
|
||||
rollback := &deployapi.DeploymentConfigRollback{
|
||||
Name: config.Name,
|
||||
UpdatedAnnotations: updatedAnnotations,
|
||||
Spec: deployapi.DeploymentConfigRollbackSpec{
|
||||
Revision: toRevision,
|
||||
IncludeTemplate: true,
|
||||
},
|
||||
}
|
||||
|
||||
rolledback, err := r.dn.DeploymentConfigs(config.Namespace).Rollback(rollback)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = r.dn.DeploymentConfigs(config.Namespace).Update(rolledback)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "rolled back", nil
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
"github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
// NewDeploymentConfigScaler returns a new scaler for deploymentConfigs
|
||||
func NewDeploymentConfigScaler(oc client.Interface, kc kclient.Interface) kubectl.Scaler {
|
||||
return &DeploymentConfigScaler{rcClient: kc, dcClient: oc, clientInterface: kc}
|
||||
}
|
||||
|
||||
// DeploymentConfigScaler is a wrapper for the kubectl Scaler client
|
||||
type DeploymentConfigScaler struct {
|
||||
rcClient kclient.ReplicationControllersNamespacer
|
||||
dcClient client.DeploymentConfigsNamespacer
|
||||
|
||||
clientInterface kclient.Interface
|
||||
}
|
||||
|
||||
// Scale updates the DeploymentConfig with the provided namespace/name, to a
|
||||
// new size, with optional precondition check (if preconditions is not nil),
|
||||
// optional retries (if retry is not nil), and then optionally waits for its
|
||||
// deployment replica count to reach the new value (if wait is not nil).
|
||||
func (scaler *DeploymentConfigScaler) Scale(namespace, name string, newSize uint, preconditions *kubectl.ScalePrecondition, retry, waitForReplicas *kubectl.RetryParams) error {
|
||||
if preconditions == nil {
|
||||
preconditions = &kubectl.ScalePrecondition{Size: -1, ResourceVersion: ""}
|
||||
}
|
||||
if retry == nil {
|
||||
// Make it try only once, immediately
|
||||
retry = &kubectl.RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
|
||||
}
|
||||
cond := kubectl.ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
|
||||
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: convert to a watch and use resource version from the ScaleCondition - kubernetes/kubernetes#31051
|
||||
if waitForReplicas != nil {
|
||||
dc, err := scaler.dcClient.DeploymentConfigs(namespace).Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rc, err := scaler.rcClient.ReplicationControllers(namespace).Get(util.LatestDeploymentNameForConfig(dc))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, controllerHasSpecifiedReplicas(scaler.clientInterface, rc, dc.Spec.Replicas))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ScaleSimple does a simple one-shot attempt at scaling - not useful on its
|
||||
// own, but a necessary building block for Scale.
|
||||
func (scaler *DeploymentConfigScaler) ScaleSimple(namespace, name string, preconditions *kubectl.ScalePrecondition, newSize uint) (string, error) {
|
||||
scale, err := scaler.dcClient.DeploymentConfigs(namespace).GetScale(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
scale.Spec.Replicas = int32(newSize)
|
||||
updated, err := scaler.dcClient.DeploymentConfigs(namespace).UpdateScale(scale)
|
||||
if err != nil {
|
||||
return "", kubectl.ScaleError{FailureType: kubectl.ScaleUpdateFailure, ResourceVersion: "Unknown", ActualError: err}
|
||||
}
|
||||
return updated.ResourceVersion, nil
|
||||
}
|
||||
|
||||
// controllerHasSpecifiedReplicas returns a condition that will be true if and
|
||||
// only if the specified replica count for a controller's ReplicaSelector
|
||||
// equals the Replicas count.
|
||||
//
|
||||
// This is a slightly modified version of
|
||||
// unversioned.ControllerHasDesiredReplicas. This is necessary because when
|
||||
// scaling an RC via a DC, the RC spec replica count is not immediately
|
||||
// updated to match the owning DC.
|
||||
func controllerHasSpecifiedReplicas(c kclient.Interface, controller *kapi.ReplicationController, specifiedReplicas int32) wait.ConditionFunc {
|
||||
// If we're given a controller where the status lags the spec, it either means that the controller is stale,
|
||||
// or that the rc manager hasn't noticed the update yet. Polling status.Replicas is not safe in the latter case.
|
||||
desiredGeneration := controller.Generation
|
||||
|
||||
return func() (bool, error) {
|
||||
ctrl, err := c.ReplicationControllers(controller.Namespace).Get(controller.Name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// There's a chance a concurrent update modifies the Spec.Replicas causing this check to pass,
|
||||
// or, after this check has passed, a modification causes the rc manager to create more pods.
|
||||
// This will not be an issue once we've implemented graceful delete for rcs, but till then
|
||||
// concurrent stop operations on the same rc might have unintended side effects.
|
||||
return ctrl.Status.ObservedGeneration >= desiredGeneration && ctrl.Status.Replicas == specifiedReplicas, nil
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
deployutil "github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
func NewDeploymentConfigStatusViewer(oc client.Interface) kubectl.StatusViewer {
|
||||
return &DeploymentConfigStatusViewer{dn: oc}
|
||||
}
|
||||
|
||||
// DeploymentConfigStatusViewer is an implementation of the kubectl StatusViewer interface
|
||||
// for deployment configs.
|
||||
type DeploymentConfigStatusViewer struct {
|
||||
dn client.DeploymentConfigsNamespacer
|
||||
}
|
||||
|
||||
var _ kubectl.StatusViewer = &DeploymentConfigStatusViewer{}
|
||||
|
||||
// Status returns a message describing deployment status, and a bool value indicating if the status is considered done
|
||||
func (s *DeploymentConfigStatusViewer) Status(namespace, name string, desiredRevision int64) (string, bool, error) {
|
||||
config, err := s.dn.DeploymentConfigs(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
latestRevision := config.Status.LatestVersion
|
||||
|
||||
if latestRevision == 0 {
|
||||
switch {
|
||||
case deployutil.HasImageChangeTrigger(config):
|
||||
return fmt.Sprintf("Deployment config %q waiting on image update\n", name), false, nil
|
||||
|
||||
case len(config.Spec.Triggers) == 0:
|
||||
return "", true, fmt.Errorf("Deployment config %q waiting on manual update (use 'oc rollout latest %s')", name, name)
|
||||
}
|
||||
}
|
||||
|
||||
if desiredRevision > 0 && latestRevision != desiredRevision {
|
||||
return "", false, fmt.Errorf("desired revision (%d) is different from the running revision (%d)", desiredRevision, latestRevision)
|
||||
}
|
||||
|
||||
cond := deployutil.GetDeploymentCondition(config.Status, deployapi.DeploymentProgressing)
|
||||
|
||||
if config.Generation <= config.Status.ObservedGeneration {
|
||||
switch {
|
||||
case cond != nil && cond.Reason == deployutil.NewRcAvailableReason:
|
||||
return fmt.Sprintf("%s\n", cond.Message), true, nil
|
||||
|
||||
case cond != nil && cond.Reason == deployutil.TimedOutReason:
|
||||
return "", true, errors.New(cond.Message)
|
||||
|
||||
case cond != nil && cond.Reason == deployutil.PausedDeployReason:
|
||||
return "", true, fmt.Errorf("Deployment config %q is paused. Resume to continue watching the status of the rollout.\n", config.Name)
|
||||
|
||||
case config.Status.UpdatedReplicas < config.Spec.Replicas:
|
||||
return fmt.Sprintf("Waiting for rollout to finish: %d out of %d new replicas have been updated...\n", config.Status.UpdatedReplicas, config.Spec.Replicas), false, nil
|
||||
|
||||
case config.Status.Replicas > config.Status.UpdatedReplicas:
|
||||
return fmt.Sprintf("Waiting for rollout to finish: %d old replicas are pending termination...\n", config.Status.Replicas-config.Status.UpdatedReplicas), false, nil
|
||||
|
||||
case config.Status.AvailableReplicas < config.Status.UpdatedReplicas:
|
||||
return fmt.Sprintf("Waiting for rollout to finish: %d of %d updated replicas are available...\n", config.Status.AvailableReplicas, config.Status.UpdatedReplicas), false, nil
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("Waiting for latest deployment config spec to be observed by the controller loop...\n"), false, nil
|
||||
}
|
||||
+630
@@ -0,0 +1,630 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
kdeplutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
"github.com/openshift/origin/pkg/util/namer"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
)
|
||||
|
||||
const (
|
||||
// Reasons for deployment config conditions:
|
||||
//
|
||||
// ReplicationControllerUpdatedReason is added in a deployment config when one of its replication
|
||||
// controllers is updated as part of the rollout process.
|
||||
ReplicationControllerUpdatedReason = "ReplicationControllerUpdated"
|
||||
// FailedRcCreateReason is added in a deployment config when it cannot create a new replication
|
||||
// controller.
|
||||
FailedRcCreateReason = "ReplicationControllerCreateError"
|
||||
// NewReplicationControllerReason is added in a deployment config when it creates a new replication
|
||||
// controller.
|
||||
NewReplicationControllerReason = "NewReplicationControllerCreated"
|
||||
// NewRcAvailableReason is added in a deployment config when its newest replication controller is made
|
||||
// available ie. the number of new pods that have passed readiness checks and run for at least
|
||||
// minReadySeconds is at least the minimum available pods that need to run for the deployment config.
|
||||
NewRcAvailableReason = "NewReplicationControllerAvailable"
|
||||
// TimedOutReason is added in a deployment config when its newest replication controller fails to show
|
||||
// any progress within the given deadline (progressDeadlineSeconds).
|
||||
TimedOutReason = "ProgressDeadlineExceeded"
|
||||
// PausedDeployReason is added in a deployment config when it is paused. Lack of progress shouldn't be
|
||||
// estimated once a deployment config is paused.
|
||||
PausedDeployReason = "DeploymentConfigPaused"
|
||||
// ResumedDeployReason is added in a deployment config when it is resumed. Useful for not failing accidentally
|
||||
// deployment configs that paused amidst a rollout.
|
||||
ResumedDeployReason = "DeploymentConfigResumed"
|
||||
)
|
||||
|
||||
// NewDeploymentCondition creates a new deployment condition.
|
||||
func NewDeploymentCondition(condType deployapi.DeploymentConditionType, status api.ConditionStatus, reason, message string) *deployapi.DeploymentCondition {
|
||||
return &deployapi.DeploymentCondition{
|
||||
Type: condType,
|
||||
Status: status,
|
||||
LastTransitionTime: unversioned.Now(),
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// GetDeploymentCondition returns the condition with the provided type.
|
||||
func GetDeploymentCondition(status deployapi.DeploymentConfigStatus, condType deployapi.DeploymentConditionType) *deployapi.DeploymentCondition {
|
||||
for i := range status.Conditions {
|
||||
c := status.Conditions[i]
|
||||
if c.Type == condType {
|
||||
return &c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDeploymentCondition updates the deployment to include the provided condition. If the condition that
|
||||
// we are about to add already exists and has the same status and reason then we are not going to update.
|
||||
func SetDeploymentCondition(status *deployapi.DeploymentConfigStatus, condition deployapi.DeploymentCondition) {
|
||||
currentCond := GetDeploymentCondition(*status, condition.Type)
|
||||
if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason {
|
||||
return
|
||||
}
|
||||
// Preserve lastTransitionTime if we are not switching between statuses of a condition.
|
||||
if currentCond != nil && currentCond.Status == condition.Status {
|
||||
condition.LastTransitionTime = currentCond.LastTransitionTime
|
||||
}
|
||||
newConditions := filterOutCondition(status.Conditions, condition.Type)
|
||||
status.Conditions = append(newConditions, condition)
|
||||
}
|
||||
|
||||
// RemoveDeploymentCondition removes the deployment condition with the provided type.
|
||||
func RemoveDeploymentCondition(status *deployapi.DeploymentConfigStatus, condType deployapi.DeploymentConditionType) {
|
||||
status.Conditions = filterOutCondition(status.Conditions, condType)
|
||||
}
|
||||
|
||||
// filterOutCondition returns a new slice of deployment conditions without conditions with the provided type.
|
||||
func filterOutCondition(conditions []deployapi.DeploymentCondition, condType deployapi.DeploymentConditionType) []deployapi.DeploymentCondition {
|
||||
var newConditions []deployapi.DeploymentCondition
|
||||
for _, c := range conditions {
|
||||
if c.Type == condType {
|
||||
continue
|
||||
}
|
||||
newConditions = append(newConditions, c)
|
||||
}
|
||||
return newConditions
|
||||
}
|
||||
|
||||
// LatestDeploymentNameForConfig returns a stable identifier for config based on its version.
|
||||
func LatestDeploymentNameForConfig(config *deployapi.DeploymentConfig) string {
|
||||
return fmt.Sprintf("%s-%d", config.Name, config.Status.LatestVersion)
|
||||
}
|
||||
|
||||
// LatestDeploymentInfo returns info about the latest deployment for a config,
|
||||
// or nil if there is no latest deployment. The latest deployment is not
|
||||
// always the same as the active deployment.
|
||||
func LatestDeploymentInfo(config *deployapi.DeploymentConfig, deployments []api.ReplicationController) (bool, *api.ReplicationController) {
|
||||
if config.Status.LatestVersion == 0 || len(deployments) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
sort.Sort(ByLatestVersionDesc(deployments))
|
||||
candidate := &deployments[0]
|
||||
return DeploymentVersionFor(candidate) == config.Status.LatestVersion, candidate
|
||||
}
|
||||
|
||||
// ActiveDeployment returns the latest complete deployment, or nil if there is
|
||||
// no such deployment. The active deployment is not always the same as the
|
||||
// latest deployment.
|
||||
func ActiveDeployment(input []api.ReplicationController) *api.ReplicationController {
|
||||
var activeDeployment *api.ReplicationController
|
||||
var lastCompleteDeploymentVersion int64 = 0
|
||||
for i := range input {
|
||||
deployment := &input[i]
|
||||
deploymentVersion := DeploymentVersionFor(deployment)
|
||||
if IsCompleteDeployment(deployment) && deploymentVersion > lastCompleteDeploymentVersion {
|
||||
activeDeployment = deployment
|
||||
lastCompleteDeploymentVersion = deploymentVersion
|
||||
}
|
||||
}
|
||||
return activeDeployment
|
||||
}
|
||||
|
||||
// DeployerPodSuffix is the suffix added to pods created from a deployment
|
||||
const DeployerPodSuffix = "deploy"
|
||||
|
||||
// DeployerPodNameForDeployment returns the name of a pod for a given deployment
|
||||
func DeployerPodNameForDeployment(deployment string) string {
|
||||
return namer.GetPodName(deployment, DeployerPodSuffix)
|
||||
}
|
||||
|
||||
// LabelForDeployment builds a string identifier for a Deployment.
|
||||
func LabelForDeployment(deployment *api.ReplicationController) string {
|
||||
return fmt.Sprintf("%s/%s", deployment.Namespace, deployment.Name)
|
||||
}
|
||||
|
||||
// LabelForDeploymentConfig builds a string identifier for a DeploymentConfig.
|
||||
func LabelForDeploymentConfig(config *deployapi.DeploymentConfig) string {
|
||||
return fmt.Sprintf("%s/%s", config.Namespace, config.Name)
|
||||
}
|
||||
|
||||
// DeploymentNameForConfigVersion returns the name of the version-th deployment
|
||||
// for the config that has the provided name
|
||||
func DeploymentNameForConfigVersion(name string, version int64) string {
|
||||
return fmt.Sprintf("%s-%d", name, version)
|
||||
}
|
||||
|
||||
// ConfigSelector returns a label Selector which can be used to find all
|
||||
// deployments for a DeploymentConfig.
|
||||
//
|
||||
// TODO: Using the annotation constant for now since the value is correct
|
||||
// but we could consider adding a new constant to the public types.
|
||||
func ConfigSelector(name string) labels.Selector {
|
||||
return labels.Set{deployapi.DeploymentConfigAnnotation: name}.AsSelector()
|
||||
}
|
||||
|
||||
// DeployerPodSelector returns a label Selector which can be used to find all
|
||||
// deployer pods associated with a deployment with name.
|
||||
func DeployerPodSelector(name string) labels.Selector {
|
||||
return labels.Set{deployapi.DeployerPodForDeploymentLabel: name}.AsSelector()
|
||||
}
|
||||
|
||||
// AnyDeployerPodSelector returns a label Selector which can be used to find
|
||||
// all deployer pods across all deployments, including hook and custom
|
||||
// deployer pods.
|
||||
func AnyDeployerPodSelector() labels.Selector {
|
||||
sel, _ := labels.Parse(deployapi.DeployerPodForDeploymentLabel)
|
||||
return sel
|
||||
}
|
||||
|
||||
// HasChangeTrigger returns whether the provided deployment configuration has
|
||||
// a config change trigger or not
|
||||
func HasChangeTrigger(config *deployapi.DeploymentConfig) bool {
|
||||
for _, trigger := range config.Spec.Triggers {
|
||||
if trigger.Type == deployapi.DeploymentTriggerOnConfigChange {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasImageChangeTrigger returns whether the provided deployment configuration has
|
||||
// an image change trigger or not.
|
||||
func HasImageChangeTrigger(config *deployapi.DeploymentConfig) bool {
|
||||
for _, trigger := range config.Spec.Triggers {
|
||||
if trigger.Type == deployapi.DeploymentTriggerOnImageChange {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func DeploymentConfigDeepCopy(dc *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
|
||||
objCopy, err := api.Scheme.DeepCopy(dc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copied, ok := objCopy.(*deployapi.DeploymentConfig)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected DeploymentConfig, got %#v", objCopy)
|
||||
}
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
func DeploymentDeepCopy(rc *api.ReplicationController) (*api.ReplicationController, error) {
|
||||
objCopy, err := api.Scheme.DeepCopy(rc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copied, ok := objCopy.(*api.ReplicationController)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected ReplicationController, got %#v", objCopy)
|
||||
}
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
// DecodeDeploymentConfig decodes a DeploymentConfig from controller using codec. An error is returned
|
||||
// if the controller doesn't contain an encoded config.
|
||||
func DecodeDeploymentConfig(controller *api.ReplicationController, decoder runtime.Decoder) (*deployapi.DeploymentConfig, error) {
|
||||
encodedConfig := []byte(EncodedDeploymentConfigFor(controller))
|
||||
decoded, err := runtime.Decode(decoder, encodedConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode DeploymentConfig from controller: %v", err)
|
||||
}
|
||||
config, ok := decoded.(*deployapi.DeploymentConfig)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("decoded object from controller is not a DeploymentConfig")
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// EncodeDeploymentConfig encodes config as a string using codec.
|
||||
func EncodeDeploymentConfig(config *deployapi.DeploymentConfig, codec runtime.Codec) (string, error) {
|
||||
bytes, err := runtime.Encode(codec, config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes[:]), nil
|
||||
}
|
||||
|
||||
// MakeDeployment creates a deployment represented as a ReplicationController and based on the given
|
||||
// DeploymentConfig. The controller replica count will be zero.
|
||||
func MakeDeployment(config *deployapi.DeploymentConfig, codec runtime.Codec) (*api.ReplicationController, error) {
|
||||
var err error
|
||||
var encodedConfig string
|
||||
|
||||
if encodedConfig, err = EncodeDeploymentConfig(config, codec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deploymentName := LatestDeploymentNameForConfig(config)
|
||||
|
||||
podSpec := api.PodSpec{}
|
||||
if err := api.Scheme.Convert(&config.Spec.Template.Spec, &podSpec, nil); err != nil {
|
||||
return nil, fmt.Errorf("couldn't clone podSpec: %v", err)
|
||||
}
|
||||
|
||||
controllerLabels := make(labels.Set)
|
||||
for k, v := range config.Labels {
|
||||
controllerLabels[k] = v
|
||||
}
|
||||
// Correlate the deployment with the config.
|
||||
// TODO: Using the annotation constant for now since the value is correct
|
||||
// but we could consider adding a new constant to the public types.
|
||||
controllerLabels[deployapi.DeploymentConfigAnnotation] = config.Name
|
||||
|
||||
// Ensure that pods created by this deployment controller can be safely associated back
|
||||
// to the controller, and that multiple deployment controllers for the same config don't
|
||||
// manipulate each others' pods.
|
||||
selector := map[string]string{}
|
||||
for k, v := range config.Spec.Selector {
|
||||
selector[k] = v
|
||||
}
|
||||
selector[deployapi.DeploymentConfigLabel] = config.Name
|
||||
selector[deployapi.DeploymentLabel] = deploymentName
|
||||
|
||||
podLabels := make(labels.Set)
|
||||
for k, v := range config.Spec.Template.Labels {
|
||||
podLabels[k] = v
|
||||
}
|
||||
podLabels[deployapi.DeploymentConfigLabel] = config.Name
|
||||
podLabels[deployapi.DeploymentLabel] = deploymentName
|
||||
|
||||
podAnnotations := make(labels.Set)
|
||||
for k, v := range config.Spec.Template.Annotations {
|
||||
podAnnotations[k] = v
|
||||
}
|
||||
podAnnotations[deployapi.DeploymentAnnotation] = deploymentName
|
||||
podAnnotations[deployapi.DeploymentConfigAnnotation] = config.Name
|
||||
podAnnotations[deployapi.DeploymentVersionAnnotation] = strconv.FormatInt(config.Status.LatestVersion, 10)
|
||||
|
||||
deployment := &api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: deploymentName,
|
||||
Namespace: config.Namespace,
|
||||
Annotations: map[string]string{
|
||||
deployapi.DeploymentConfigAnnotation: config.Name,
|
||||
deployapi.DeploymentStatusAnnotation: string(deployapi.DeploymentStatusNew),
|
||||
deployapi.DeploymentEncodedConfigAnnotation: encodedConfig,
|
||||
deployapi.DeploymentVersionAnnotation: strconv.FormatInt(config.Status.LatestVersion, 10),
|
||||
// This is the target replica count for the new deployment.
|
||||
deployapi.DesiredReplicasAnnotation: strconv.Itoa(int(config.Spec.Replicas)),
|
||||
deployapi.DeploymentReplicasAnnotation: strconv.Itoa(0),
|
||||
},
|
||||
Labels: controllerLabels,
|
||||
},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
// The deployment should be inactive initially
|
||||
Replicas: 0,
|
||||
Selector: selector,
|
||||
Template: &api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: podLabels,
|
||||
Annotations: podAnnotations,
|
||||
},
|
||||
Spec: podSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
if config.Status.Details != nil && len(config.Status.Details.Message) > 0 {
|
||||
deployment.Annotations[deployapi.DeploymentStatusReasonAnnotation] = config.Status.Details.Message
|
||||
}
|
||||
if value, ok := config.Annotations[deployapi.DeploymentIgnorePodAnnotation]; ok {
|
||||
deployment.Annotations[deployapi.DeploymentIgnorePodAnnotation] = value
|
||||
}
|
||||
|
||||
return deployment, nil
|
||||
}
|
||||
|
||||
// GetReplicaCountForDeployments returns the sum of all replicas for the
|
||||
// given deployments.
|
||||
func GetReplicaCountForDeployments(deployments []api.ReplicationController) int32 {
|
||||
totalReplicaCount := int32(0)
|
||||
for _, deployment := range deployments {
|
||||
totalReplicaCount += deployment.Spec.Replicas
|
||||
}
|
||||
return totalReplicaCount
|
||||
}
|
||||
|
||||
// GetStatusReplicaCountForDeployments returns the sum of the replicas reported in the
|
||||
// status of the given deployments.
|
||||
func GetStatusReplicaCountForDeployments(deployments []api.ReplicationController) int32 {
|
||||
totalReplicaCount := int32(0)
|
||||
for _, deployment := range deployments {
|
||||
totalReplicaCount += deployment.Status.Replicas
|
||||
}
|
||||
return totalReplicaCount
|
||||
}
|
||||
|
||||
// GetAvailablePods returns all the available pods from the provided pod list.
|
||||
func GetAvailablePods(pods []*api.Pod, minReadySeconds int32) int32 {
|
||||
available := int32(0)
|
||||
for i := range pods {
|
||||
pod := pods[i]
|
||||
if kdeplutil.IsPodAvailable(pod, minReadySeconds, time.Now()) {
|
||||
available++
|
||||
}
|
||||
}
|
||||
return available
|
||||
}
|
||||
|
||||
func DeploymentConfigNameFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentConfigAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentNameFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentAnnotation)
|
||||
}
|
||||
|
||||
func DeployerPodNameFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentPodAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentStatusFor(obj runtime.Object) deployapi.DeploymentStatus {
|
||||
return deployapi.DeploymentStatus(annotationFor(obj, deployapi.DeploymentStatusAnnotation))
|
||||
}
|
||||
|
||||
func DeploymentStatusReasonFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentStatusReasonAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentDesiredReplicas(obj runtime.Object) (int32, bool) {
|
||||
return int32AnnotationFor(obj, deployapi.DesiredReplicasAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentReplicas(obj runtime.Object) (int32, bool) {
|
||||
return int32AnnotationFor(obj, deployapi.DeploymentReplicasAnnotation)
|
||||
}
|
||||
|
||||
func EncodedDeploymentConfigFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentEncodedConfigAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentVersionFor(obj runtime.Object) int64 {
|
||||
v, err := strconv.ParseInt(annotationFor(obj, deployapi.DeploymentVersionAnnotation), 10, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func IsDeploymentCancelled(deployment *api.ReplicationController) bool {
|
||||
value := annotationFor(deployment, deployapi.DeploymentCancelledAnnotation)
|
||||
return strings.EqualFold(value, deployapi.DeploymentCancelledAnnotationValue)
|
||||
}
|
||||
|
||||
// HasSynced checks if the provided deployment config has been noticed by the deployment
|
||||
// config controller.
|
||||
func HasSynced(dc *deployapi.DeploymentConfig, generation int64) bool {
|
||||
return dc.Status.ObservedGeneration >= generation
|
||||
}
|
||||
|
||||
// IsOwnedByConfig checks whether the provided replication controller is part of a
|
||||
// deployment configuration.
|
||||
// TODO: Switch to use owner references once we got those working.
|
||||
func IsOwnedByConfig(deployment *api.ReplicationController) bool {
|
||||
_, ok := deployment.Annotations[deployapi.DeploymentConfigAnnotation]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsTerminatedDeployment returns true if the passed deployment has terminated (either
|
||||
// complete or failed).
|
||||
func IsTerminatedDeployment(deployment *api.ReplicationController) bool {
|
||||
return IsCompleteDeployment(deployment) || IsFailedDeployment(deployment)
|
||||
}
|
||||
|
||||
// IsCompleteDeployment returns true if the passed deployment failed.
|
||||
func IsCompleteDeployment(deployment *api.ReplicationController) bool {
|
||||
current := DeploymentStatusFor(deployment)
|
||||
return current == deployapi.DeploymentStatusComplete
|
||||
}
|
||||
|
||||
// IsFailedDeployment returns true if the passed deployment failed.
|
||||
func IsFailedDeployment(deployment *api.ReplicationController) bool {
|
||||
current := DeploymentStatusFor(deployment)
|
||||
return current == deployapi.DeploymentStatusFailed
|
||||
}
|
||||
|
||||
// CanTransitionPhase returns whether it is allowed to go from the current to the next phase.
|
||||
func CanTransitionPhase(current, next deployapi.DeploymentStatus) bool {
|
||||
switch current {
|
||||
case deployapi.DeploymentStatusNew:
|
||||
switch next {
|
||||
case deployapi.DeploymentStatusPending,
|
||||
deployapi.DeploymentStatusRunning,
|
||||
deployapi.DeploymentStatusFailed,
|
||||
deployapi.DeploymentStatusComplete:
|
||||
return true
|
||||
}
|
||||
case deployapi.DeploymentStatusPending:
|
||||
switch next {
|
||||
case deployapi.DeploymentStatusRunning,
|
||||
deployapi.DeploymentStatusFailed,
|
||||
deployapi.DeploymentStatusComplete:
|
||||
return true
|
||||
}
|
||||
case deployapi.DeploymentStatusRunning:
|
||||
switch next {
|
||||
case deployapi.DeploymentStatusFailed, deployapi.DeploymentStatusComplete:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRollingConfig returns true if the strategy type is a rolling update.
|
||||
func IsRollingConfig(config *deployapi.DeploymentConfig) bool {
|
||||
return config.Spec.Strategy.Type == deployapi.DeploymentStrategyTypeRolling
|
||||
}
|
||||
|
||||
// IsProgressing expects a state deployment config and its updated status in order to
|
||||
// determine if there is any progress.
|
||||
func IsProgressing(config deployapi.DeploymentConfig, newStatus deployapi.DeploymentConfigStatus) bool {
|
||||
oldStatusOldReplicas := config.Status.Replicas - config.Status.UpdatedReplicas
|
||||
newStatusOldReplicas := newStatus.Replicas - newStatus.UpdatedReplicas
|
||||
|
||||
return (newStatus.UpdatedReplicas > config.Status.UpdatedReplicas) || (newStatusOldReplicas < oldStatusOldReplicas)
|
||||
}
|
||||
|
||||
// MaxUnavailable returns the maximum unavailable pods a rolling deployment config can take.
|
||||
func MaxUnavailable(config deployapi.DeploymentConfig) int32 {
|
||||
if !IsRollingConfig(&config) {
|
||||
return int32(0)
|
||||
}
|
||||
// Error caught by validation
|
||||
_, maxUnavailable, _ := kdeplutil.ResolveFenceposts(&config.Spec.Strategy.RollingParams.MaxSurge, &config.Spec.Strategy.RollingParams.MaxUnavailable, config.Spec.Replicas)
|
||||
return maxUnavailable
|
||||
}
|
||||
|
||||
// MaxSurge returns the maximum surge pods a rolling deployment config can take.
|
||||
func MaxSurge(config deployapi.DeploymentConfig) int32 {
|
||||
if !IsRollingConfig(&config) {
|
||||
return int32(0)
|
||||
}
|
||||
// Error caught by validation
|
||||
maxSurge, _, _ := kdeplutil.ResolveFenceposts(&config.Spec.Strategy.RollingParams.MaxSurge, &config.Spec.Strategy.RollingParams.MaxUnavailable, config.Spec.Replicas)
|
||||
return maxSurge
|
||||
}
|
||||
|
||||
// annotationFor returns the annotation with key for obj.
|
||||
func annotationFor(obj runtime.Object, key string) string {
|
||||
meta, err := api.ObjectMetaFor(obj)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return meta.Annotations[key]
|
||||
}
|
||||
|
||||
func int32AnnotationFor(obj runtime.Object, key string) (int32, bool) {
|
||||
s := annotationFor(obj, key)
|
||||
if len(s) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
i, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return int32(i), true
|
||||
}
|
||||
|
||||
// DeploymentsForCleanup determines which deployments for a configuration are relevant for the
|
||||
// revision history limit quota
|
||||
func DeploymentsForCleanup(configuration *deployapi.DeploymentConfig, deployments []api.ReplicationController) []api.ReplicationController {
|
||||
// if the past deployment quota has been exceeded, we need to prune the oldest deployments
|
||||
// until we are not exceeding the quota any longer, so we sort oldest first
|
||||
sort.Sort(ByLatestVersionAsc(deployments))
|
||||
|
||||
relevantDeployments := []api.ReplicationController{}
|
||||
activeDeployment := ActiveDeployment(deployments)
|
||||
if activeDeployment == nil {
|
||||
// if cleanup policy is set but no successful deployments have happened, there will be
|
||||
// no active deployment. We can consider all of the deployments in this case except for
|
||||
// the latest one
|
||||
for i := range deployments {
|
||||
deployment := &deployments[i]
|
||||
if DeploymentVersionFor(deployment) != configuration.Status.LatestVersion {
|
||||
relevantDeployments = append(relevantDeployments, *deployment)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if there is an active deployment, we need to filter out any deployments that we don't
|
||||
// care about, namely the active deployment and any newer deployments
|
||||
for i := range deployments {
|
||||
deployment := &deployments[i]
|
||||
if deployment != activeDeployment && DeploymentVersionFor(deployment) < DeploymentVersionFor(activeDeployment) {
|
||||
relevantDeployments = append(relevantDeployments, *deployment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return relevantDeployments
|
||||
}
|
||||
|
||||
// WaitForRunningDeployerPod waits a given period of time until the deployer pod
|
||||
// for given replication controller is not running.
|
||||
func WaitForRunningDeployerPod(podClient kclient.PodsNamespacer, rc *api.ReplicationController, timeout time.Duration) error {
|
||||
podName := DeployerPodNameForDeployment(rc.Name)
|
||||
canGetLogs := func(p *api.Pod) bool {
|
||||
return api.PodSucceeded == p.Status.Phase || api.PodFailed == p.Status.Phase || api.PodRunning == p.Status.Phase
|
||||
}
|
||||
pod, err := podClient.Pods(rc.Namespace).Get(podName)
|
||||
if err == nil && canGetLogs(pod) {
|
||||
return nil
|
||||
}
|
||||
watcher, err := podClient.Pods(rc.Namespace).Watch(
|
||||
api.ListOptions{
|
||||
FieldSelector: fields.Set{"metadata.name": podName}.AsSelector(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer watcher.Stop()
|
||||
if _, err := watch.Until(timeout, watcher, func(e watch.Event) (bool, error) {
|
||||
if e.Type == watch.Error {
|
||||
return false, fmt.Errorf("encountered error while watching for pod: %v", e.Object)
|
||||
}
|
||||
obj, isPod := e.Object.(*api.Pod)
|
||||
if !isPod {
|
||||
return false, errors.New("received unknown object while watching for pods")
|
||||
}
|
||||
return canGetLogs(obj), nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ByLatestVersionAsc sorts deployments by LatestVersion ascending.
|
||||
type ByLatestVersionAsc []api.ReplicationController
|
||||
|
||||
func (d ByLatestVersionAsc) Len() int { return len(d) }
|
||||
func (d ByLatestVersionAsc) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
func (d ByLatestVersionAsc) Less(i, j int) bool {
|
||||
return DeploymentVersionFor(&d[i]) < DeploymentVersionFor(&d[j])
|
||||
}
|
||||
|
||||
// ByLatestVersionDesc sorts deployments by LatestVersion descending.
|
||||
type ByLatestVersionDesc []api.ReplicationController
|
||||
|
||||
func (d ByLatestVersionDesc) Len() int { return len(d) }
|
||||
func (d ByLatestVersionDesc) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
func (d ByLatestVersionDesc) Less(i, j int) bool {
|
||||
return DeploymentVersionFor(&d[j]) < DeploymentVersionFor(&d[i])
|
||||
}
|
||||
|
||||
// ByMostRecent sorts deployments by most recently created.
|
||||
type ByMostRecent []*api.ReplicationController
|
||||
|
||||
func (s ByMostRecent) Len() int { return len(s) }
|
||||
func (s ByMostRecent) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s ByMostRecent) Less(i, j int) bool {
|
||||
return !s[i].CreationTimestamp.Before(s[j].CreationTimestamp)
|
||||
}
|
||||
Reference in New Issue
Block a user