Files
PandaX/apps/devops/services/k8s/deployment/deployment_detail.go
2022-01-22 17:07:04 +08:00

206 lines
6.7 KiB
Go

package deployment
import (
"context"
"fmt"
"pandax/base/global"
"pandax/base/utils"
apps "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
k8scommon "pandax/apps/devops/services/k8s/common"
"pandax/apps/devops/services/k8s/event"
"pandax/apps/devops/services/k8s/service"
"sort"
)
// RollingUpdateStrategy is behavior of a rolling update. See RollingUpdateDeployment K8s object.
type RollingUpdateStrategy struct {
MaxSurge *intstr.IntOrString `json:"maxSurge"`
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable"`
}
// StatusInfo is the status information of the deployment
type StatusInfo struct {
// Total number of desired replicas on the deployment
Replicas int32 `json:"replicas"`
// Number of non-terminated pods that have the desired template spec
Updated int32 `json:"updated"`
// Number of available pods (ready for at least minReadySeconds)
// targeted by this deployment
Available int32 `json:"available"`
// Total number of unavailable pods targeted by this deployment.
Unavailable int32 `json:"unavailable"`
}
// DeploymentDetail is a presentation layer view of Kubernetes Deployment resource.
type DeploymentDetail struct {
// Extends list item structure.
Deployment `json:",inline"`
// Label selector of the service.
Selector map[string]string `json:"selector"`
// Status information on the deployment
StatusInfo `json:"statusInfo"`
// Conditions describe the state of a deployment at a certain point.
Conditions []k8scommon.Condition `json:"conditions"`
// The deployment strategy to use to replace existing pods with new ones.
// Valid options: Recreate, RollingUpdate
Strategy apps.DeploymentStrategyType `json:"strategy"`
// Min ready seconds
MinReadySeconds int32 `json:"minReadySeconds"`
// Rolling update strategy containing maxSurge and maxUnavailable
RollingUpdateStrategy *RollingUpdateStrategy `json:"rollingUpdateStrategy,omitempty"`
// Optional field that specifies the number of old Replica Sets to retain to allow rollback.
RevisionHistoryLimit *int32 `json:"revisionHistoryLimit"`
// Events Info
Events []v1.Event `json:"events"`
// Deployment history image version
HistoryVersion []HistoryVersion `json:"historyVersion"`
PodList *PodList `json:"podList"`
SvcList *service.ServiceList `json:"svcList"`
}
// GetDeploymentDetail returns model object of deployment and error, if any.
func GetDeploymentDetail(client *kubernetes.Clientset, namespace string, deploymentName string) (*DeploymentDetail, error) {
global.Log.Info(fmt.Sprintf("Getting details of %s deployment in %s namespace", deploymentName, namespace))
deployment, err := client.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metaV1.GetOptions{})
if err != nil {
return nil, err
}
selector, err := metaV1.LabelSelectorAsSelector(deployment.Spec.Selector)
if err != nil {
return nil, err
}
options := metaV1.ListOptions{LabelSelector: selector.String()}
channels := &k8scommon.ResourceChannels{
ReplicaSetList: k8scommon.GetReplicaSetListChannelWithOptions(client,
k8scommon.NewSameNamespaceQuery(namespace), options, 1),
PodList: k8scommon.GetPodListChannelWithOptions(client,
k8scommon.NewSameNamespaceQuery(namespace), options, 1),
EventList: k8scommon.GetEventListChannelWithOptions(client,
k8scommon.NewSameNamespaceQuery(namespace), options, 1),
}
rawRs := <-channels.ReplicaSetList.List
err = <-channels.ReplicaSetList.Error
if err != nil {
return nil, err
}
rawPods := <-channels.PodList.List
err = <-channels.PodList.Error
if err != nil {
return nil, err
}
rawEvents := <-channels.EventList.List
err = <-channels.EventList.Error
if err != nil {
return nil, err
}
// Extra Info
var rollingUpdateStrategy *RollingUpdateStrategy
if deployment.Spec.Strategy.RollingUpdate != nil {
rollingUpdateStrategy = &RollingUpdateStrategy{
MaxSurge: deployment.Spec.Strategy.RollingUpdate.MaxSurge,
MaxUnavailable: deployment.Spec.Strategy.RollingUpdate.MaxUnavailable,
}
}
events, _ := event.GetEvents(client, namespace, fmt.Sprintf("involvedObject.name=%v", deploymentName))
serviceList, _ := service.GetToService(client, namespace, deploymentName)
return &DeploymentDetail{
Deployment: toDeployment(deployment, rawRs.Items, rawPods.Items, rawEvents.Items),
Selector: deployment.Spec.Selector.MatchLabels,
StatusInfo: GetStatusInfo(&deployment.Status),
Conditions: getConditions(deployment.Status.Conditions),
Strategy: deployment.Spec.Strategy.Type,
MinReadySeconds: deployment.Spec.MinReadySeconds,
RollingUpdateStrategy: rollingUpdateStrategy,
RevisionHistoryLimit: deployment.Spec.RevisionHistoryLimit,
Events: events,
PodList: getDeploymentToPod(client, deployment),
SvcList: serviceList,
HistoryVersion: getDeploymentHistory(namespace, deploymentName, rawRs.Items),
}, nil
}
// GetStatusInfo is used to get the status information from the *apps.DeploymentStatus
func GetStatusInfo(deploymentStatus *apps.DeploymentStatus) StatusInfo {
return StatusInfo{
Replicas: deploymentStatus.Replicas,
Updated: deploymentStatus.UpdatedReplicas,
Available: deploymentStatus.AvailableReplicas,
Unavailable: deploymentStatus.UnavailableReplicas,
}
}
type HistoryVersion struct {
CreateTime metaV1.Time `json:"create_time"`
Image string `json:"image"`
Version int64 `json:"version"`
Namespace string `json:"namespace"`
Name string `json:"name"`
}
func getDeploymentHistory(namespace string, deploymentName string, rs []apps.ReplicaSet) []HistoryVersion {
var historyVersion []HistoryVersion
for _, v := range rs {
if namespace == v.Namespace && deploymentName == v.OwnerReferences[0].Name {
history := HistoryVersion{
CreateTime: v.CreationTimestamp,
Image: v.Spec.Template.Spec.Containers[0].Image,
Version: utils.ParseStringToInt64(v.Annotations["deployment.kubernetes.io/revision"]),
Namespace: v.Namespace,
Name: v.OwnerReferences[0].Name,
}
historyVersion = append(historyVersion, history)
}
}
// Sort the map by date
//sort.Slice(historyVersion, func(i, j int) bool {
// return historyVersion[j].CreateTime.Before(&historyVersion[i].CreateTime)
//})
// Sort the map by version
sort.Sort(historiesByRevision(historyVersion))
return historyVersion
}
type historiesByRevision []HistoryVersion
func (h historiesByRevision) Len() int {
return len(h)
}
func (h historiesByRevision) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
}
func (h historiesByRevision) Less(i, j int) bool {
return h[j].Version < h[i].Version
}