mirror of https://github.com/k3s-io/k3s
Merge pull request #14199 from nikhiljindal/deploymentDescribe
Adding a DeploymentDescriber to kubectlpull/6/head
commit
7adb463dd0
|
@ -18,7 +18,6 @@ package deployment
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
|
@ -28,6 +27,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
deploymentUtil "k8s.io/kubernetes/pkg/util/deployment"
|
||||
)
|
||||
|
||||
type DeploymentController struct {
|
||||
|
@ -116,59 +116,21 @@ func (d *DeploymentController) reconcileRollingUpdateDeployment(deployment exper
|
|||
}
|
||||
|
||||
func (d *DeploymentController) getOldRCs(deployment experimental.Deployment) ([]*api.ReplicationController, error) {
|
||||
namespace := deployment.ObjectMeta.Namespace
|
||||
// 1. Find all pods whose labels match deployment.Spec.Selector
|
||||
podList, err := d.client.Pods(namespace).List(labels.SelectorFromSet(deployment.Spec.Selector), fields.Everything())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing pods: %v", err)
|
||||
}
|
||||
// 2. Find the corresponding RCs for pods in podList.
|
||||
// TODO: Right now we list all RCs and then filter. We should add an API for this.
|
||||
oldRCs := map[string]api.ReplicationController{}
|
||||
rcList, err := d.client.ReplicationControllers(namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing replication controllers: %v", err)
|
||||
}
|
||||
for _, pod := range podList.Items {
|
||||
podLabelsSelector := labels.Set(pod.ObjectMeta.Labels)
|
||||
for _, rc := range rcList.Items {
|
||||
rcLabelsSelector := labels.SelectorFromSet(rc.Spec.Selector)
|
||||
if rcLabelsSelector.Matches(podLabelsSelector) {
|
||||
// Filter out RC that has the same pod template spec as the deployment - that is the new RC.
|
||||
if api.Semantic.DeepEqual(rc.Spec.Template, getNewRCTemplate(deployment)) {
|
||||
continue
|
||||
}
|
||||
oldRCs[rc.ObjectMeta.Name] = rc
|
||||
}
|
||||
}
|
||||
}
|
||||
requiredRCs := []*api.ReplicationController{}
|
||||
for _, value := range oldRCs {
|
||||
requiredRCs = append(requiredRCs, &value)
|
||||
}
|
||||
return requiredRCs, nil
|
||||
return deploymentUtil.GetOldRCs(deployment, d.client)
|
||||
}
|
||||
|
||||
// Returns an RC that matches the intent of the given deployment.
|
||||
// It creates a new RC if required.
|
||||
func (d *DeploymentController) getNewRC(deployment experimental.Deployment) (*api.ReplicationController, error) {
|
||||
namespace := deployment.ObjectMeta.Namespace
|
||||
// Find if the required RC exists already.
|
||||
rcList, err := d.client.ReplicationControllers(namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing replication controllers: %v", err)
|
||||
}
|
||||
newRCTemplate := getNewRCTemplate(deployment)
|
||||
|
||||
for _, rc := range rcList.Items {
|
||||
if api.Semantic.DeepEqual(rc.Spec.Template, newRCTemplate) {
|
||||
// This is the new RC.
|
||||
return &rc, nil
|
||||
}
|
||||
existingNewRC, err := deploymentUtil.GetNewRC(deployment, d.client)
|
||||
if err != nil || existingNewRC != nil {
|
||||
return existingNewRC, err
|
||||
}
|
||||
// new RC does not exist, create one.
|
||||
podTemplateSpecHash := getPodTemplateSpecHash(deployment.Spec.Template)
|
||||
namespace := deployment.ObjectMeta.Namespace
|
||||
podTemplateSpecHash := deploymentUtil.GetPodTemplateSpecHash(deployment.Spec.Template)
|
||||
rcName := fmt.Sprintf("deploymentrc-%d", podTemplateSpecHash)
|
||||
newRCTemplate := deploymentUtil.GetNewRCTemplate(deployment)
|
||||
newRC := api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: rcName,
|
||||
|
@ -187,30 +149,6 @@ func (d *DeploymentController) getNewRC(deployment experimental.Deployment) (*ap
|
|||
return createdRC, nil
|
||||
}
|
||||
|
||||
func getNewRCTemplate(deployment experimental.Deployment) *api.PodTemplateSpec {
|
||||
// newRC will have the same template as in deployment spec, plus a unique label in some cases.
|
||||
newRCTemplate := &api.PodTemplateSpec{
|
||||
ObjectMeta: deployment.Spec.Template.ObjectMeta,
|
||||
Spec: deployment.Spec.Template.Spec,
|
||||
}
|
||||
podTemplateSpecHash := getPodTemplateSpecHash(newRCTemplate)
|
||||
if deployment.Spec.UniqueLabelKey != "" {
|
||||
newLabels := map[string]string{}
|
||||
for key, value := range deployment.Spec.Template.ObjectMeta.Labels {
|
||||
newLabels[key] = value
|
||||
}
|
||||
newLabels[deployment.Spec.UniqueLabelKey] = fmt.Sprintf("%d", podTemplateSpecHash)
|
||||
newRCTemplate.ObjectMeta.Labels = newLabels
|
||||
}
|
||||
return newRCTemplate
|
||||
}
|
||||
|
||||
func getPodTemplateSpecHash(template *api.PodTemplateSpec) uint32 {
|
||||
podTemplateSpecHasher := adler32.New()
|
||||
util.DeepHashObject(podTemplateSpecHasher, template)
|
||||
return podTemplateSpecHasher.Sum32()
|
||||
}
|
||||
|
||||
func (d *DeploymentController) getPodsForRCs(replicationControllers []*api.ReplicationController) ([]api.Pod, error) {
|
||||
allPods := []api.Pod{}
|
||||
for _, rc := range replicationControllers {
|
||||
|
|
|
@ -35,6 +35,7 @@ import (
|
|||
qosutil "k8s.io/kubernetes/pkg/kubelet/qos/util"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
deploymentUtil "k8s.io/kubernetes/pkg/util/deployment"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
|
@ -88,6 +89,7 @@ func expDescriberMap(c *client.Client) map[string]Describer {
|
|||
"HorizontalPodAutoscaler": &HorizontalPodAutoscalerDescriber{c},
|
||||
"DaemonSet": &DaemonSetDescriber{c},
|
||||
"Job": &JobDescriber{c},
|
||||
"Deployment": &DeploymentDescriber{c},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,7 +480,11 @@ func describePod(pod *api.Pod, rcs []api.ReplicationController, events *api.Even
|
|||
fmt.Fprintf(out, "Reason:\t%s\n", pod.Status.Reason)
|
||||
fmt.Fprintf(out, "Message:\t%s\n", pod.Status.Message)
|
||||
fmt.Fprintf(out, "IP:\t%s\n", pod.Status.PodIP)
|
||||
fmt.Fprintf(out, "Replication Controllers:\t%s\n", printReplicationControllersByLabels(rcs))
|
||||
var matchingRCs []*api.ReplicationController
|
||||
for _, rc := range rcs {
|
||||
matchingRCs = append(matchingRCs, &rc)
|
||||
}
|
||||
fmt.Fprintf(out, "Replication Controllers:\t%s\n", printReplicationControllersByLabels(matchingRCs))
|
||||
fmt.Fprintf(out, "Containers:\n")
|
||||
describeContainers(pod, out)
|
||||
if len(pod.Status.Conditions) > 0 {
|
||||
|
@ -1395,6 +1401,44 @@ func DescribeEvents(el *api.EventList, w io.Writer) {
|
|||
}
|
||||
}
|
||||
|
||||
// DeploymentDescriber generates information about a deployment.
|
||||
type DeploymentDescriber struct {
|
||||
client.Interface
|
||||
}
|
||||
|
||||
func (dd *DeploymentDescriber) Describe(namespace, name string) (string, error) {
|
||||
d, err := dd.Experimental().Deployments(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tabbedString(func(out io.Writer) error {
|
||||
fmt.Fprintf(out, "Name:\t%s\n", d.ObjectMeta.Name)
|
||||
fmt.Fprintf(out, "Namespace:\t%s\n", d.ObjectMeta.Namespace)
|
||||
fmt.Fprintf(out, "CreationTimestamp:\t%s\n", d.CreationTimestamp.Time.Format(time.RFC1123Z))
|
||||
fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(d.Labels))
|
||||
fmt.Fprintf(out, "Selector:\t%s\n", labels.FormatLabels(d.Spec.Selector))
|
||||
fmt.Fprintf(out, "Replicas:\t%d updated / %d total\n", d.Status.UpdatedReplicas, d.Spec.Replicas)
|
||||
fmt.Fprintf(out, "StrategyType:\t%s\n", d.Spec.Strategy.Type)
|
||||
if d.Spec.Strategy.RollingUpdate != nil {
|
||||
ru := d.Spec.Strategy.RollingUpdate
|
||||
fmt.Fprintf(out, "RollingUpdateStrategy:\t%s max unavailable, %s max surge, %d min ready seconds\n", ru.MaxUnavailable.String(), ru.MaxSurge.String(), ru.MinReadySeconds)
|
||||
}
|
||||
oldRCs, err := deploymentUtil.GetOldRCs(*d, dd)
|
||||
if err == nil {
|
||||
fmt.Fprintf(out, "OldReplicationControllers:\t%s\n", printReplicationControllersByLabels(oldRCs))
|
||||
}
|
||||
newRC, err := deploymentUtil.GetNewRC(*d, dd)
|
||||
if err == nil {
|
||||
var newRCs []*api.ReplicationController
|
||||
if newRC != nil {
|
||||
newRCs = append(newRCs, newRC)
|
||||
}
|
||||
fmt.Fprintf(out, "NewReplicationController:\t%s\n", printReplicationControllersByLabels(newRCs))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Get all daemon set whose selectors would match a given set of labels.
|
||||
// TODO: Move this to pkg/client and ideally implement it server-side (instead
|
||||
// of getting all DS's and searching through them manually).
|
||||
|
@ -1442,7 +1486,7 @@ func getReplicationControllersForLabels(c client.ReplicationControllerInterface,
|
|||
return matchingRCs, nil
|
||||
}
|
||||
|
||||
func printReplicationControllersByLabels(matchingRCs []api.ReplicationController) string {
|
||||
func printReplicationControllersByLabels(matchingRCs []*api.ReplicationController) string {
|
||||
// Format the matching RC's into strings.
|
||||
var rcStrings []string
|
||||
for _, controller := range matchingRCs {
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
|
||||
)
|
||||
|
@ -476,6 +477,26 @@ func TestPersistentVolumeDescriber(t *testing.T) {
|
|||
if str == "" {
|
||||
t.Errorf("Unexpected empty string for test %s. Expected PV Describer output", name)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeDeployment(t *testing.T) {
|
||||
fake := testclient.NewSimpleFake(&experimental.Deployment{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "bar",
|
||||
Namespace: "foo",
|
||||
},
|
||||
Spec: experimental.DeploymentSpec{
|
||||
Template: &api.PodTemplateSpec{},
|
||||
},
|
||||
})
|
||||
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
|
||||
d := DeploymentDescriber{c}
|
||||
out, err := d.Describe("foo", "bar")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, "bar") || !strings.Contains(out, "foo") {
|
||||
t.Errorf("unexpected out: %s", out)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 deployment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
// Returns the old RCs targetted by the given Deployment.
|
||||
func GetOldRCs(deployment experimental.Deployment, c client.Interface) ([]*api.ReplicationController, error) {
|
||||
namespace := deployment.ObjectMeta.Namespace
|
||||
// 1. Find all pods whose labels match deployment.Spec.Selector
|
||||
podList, err := c.Pods(namespace).List(labels.SelectorFromSet(deployment.Spec.Selector), fields.Everything())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing pods: %v", err)
|
||||
}
|
||||
// 2. Find the corresponding RCs for pods in podList.
|
||||
// TODO: Right now we list all RCs and then filter. We should add an API for this.
|
||||
oldRCs := map[string]api.ReplicationController{}
|
||||
rcList, err := c.ReplicationControllers(namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing replication controllers: %v", err)
|
||||
}
|
||||
for _, pod := range podList.Items {
|
||||
podLabelsSelector := labels.Set(pod.ObjectMeta.Labels)
|
||||
for _, rc := range rcList.Items {
|
||||
rcLabelsSelector := labels.SelectorFromSet(rc.Spec.Selector)
|
||||
if rcLabelsSelector.Matches(podLabelsSelector) {
|
||||
// Filter out RC that has the same pod template spec as the deployment - that is the new RC.
|
||||
if api.Semantic.DeepEqual(rc.Spec.Template, GetNewRCTemplate(deployment)) {
|
||||
continue
|
||||
}
|
||||
oldRCs[rc.ObjectMeta.Name] = rc
|
||||
}
|
||||
}
|
||||
}
|
||||
requiredRCs := []*api.ReplicationController{}
|
||||
for _, value := range oldRCs {
|
||||
requiredRCs = append(requiredRCs, &value)
|
||||
}
|
||||
return requiredRCs, nil
|
||||
}
|
||||
|
||||
// Returns an RC that matches the intent of the given deployment.
|
||||
// Returns nil if the new RC doesnt exist yet.
|
||||
func GetNewRC(deployment experimental.Deployment, c client.Interface) (*api.ReplicationController, error) {
|
||||
namespace := deployment.ObjectMeta.Namespace
|
||||
rcList, err := c.ReplicationControllers(namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing replication controllers: %v", err)
|
||||
}
|
||||
newRCTemplate := GetNewRCTemplate(deployment)
|
||||
|
||||
for _, rc := range rcList.Items {
|
||||
if api.Semantic.DeepEqual(rc.Spec.Template, newRCTemplate) {
|
||||
// This is the new RC.
|
||||
return &rc, nil
|
||||
}
|
||||
}
|
||||
// new RC does not exist.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Returns the desired PodTemplateSpec for the new RC corresponding to the given RC.
|
||||
func GetNewRCTemplate(deployment experimental.Deployment) *api.PodTemplateSpec {
|
||||
// newRC will have the same template as in deployment spec, plus a unique label in some cases.
|
||||
newRCTemplate := &api.PodTemplateSpec{
|
||||
ObjectMeta: deployment.Spec.Template.ObjectMeta,
|
||||
Spec: deployment.Spec.Template.Spec,
|
||||
}
|
||||
podTemplateSpecHash := GetPodTemplateSpecHash(newRCTemplate)
|
||||
if deployment.Spec.UniqueLabelKey != "" {
|
||||
newLabels := map[string]string{}
|
||||
for key, value := range deployment.Spec.Template.ObjectMeta.Labels {
|
||||
newLabels[key] = value
|
||||
}
|
||||
newLabels[deployment.Spec.UniqueLabelKey] = fmt.Sprintf("%d", podTemplateSpecHash)
|
||||
newRCTemplate.ObjectMeta.Labels = newLabels
|
||||
}
|
||||
return newRCTemplate
|
||||
}
|
||||
|
||||
func GetPodTemplateSpecHash(template *api.PodTemplateSpec) uint32 {
|
||||
podTemplateSpecHasher := adler32.New()
|
||||
util.DeepHashObject(podTemplateSpecHasher, template)
|
||||
return podTemplateSpecHasher.Sum32()
|
||||
}
|
Loading…
Reference in New Issue