mirror of https://github.com/k3s-io/k3s
gets the correct version of kubernetes client for DaemonSet and StatefulSet History and Rollback and updates test-cmd for new versions
parent
7c772ab2ad
commit
5590c1fb94
|
@ -1390,7 +1390,7 @@ run_kubectl_get_tests() {
|
||||||
kube::test::if_has_string "${output_message}" "/api/v1/namespaces/default/pods 200 OK"
|
kube::test::if_has_string "${output_message}" "/api/v1/namespaces/default/pods 200 OK"
|
||||||
kube::test::if_has_string "${output_message}" "/api/v1/namespaces/default/replicationcontrollers 200 OK"
|
kube::test::if_has_string "${output_message}" "/api/v1/namespaces/default/replicationcontrollers 200 OK"
|
||||||
kube::test::if_has_string "${output_message}" "/api/v1/namespaces/default/services 200 OK"
|
kube::test::if_has_string "${output_message}" "/api/v1/namespaces/default/services 200 OK"
|
||||||
kube::test::if_has_string "${output_message}" "/apis/apps/v1beta1/namespaces/default/statefulsets 200 OK"
|
kube::test::if_has_string "${output_message}" "/apis/apps/v1/namespaces/default/statefulsets 200 OK"
|
||||||
kube::test::if_has_string "${output_message}" "/apis/autoscaling/v1/namespaces/default/horizontalpodautoscalers 200"
|
kube::test::if_has_string "${output_message}" "/apis/autoscaling/v1/namespaces/default/horizontalpodautoscalers 200"
|
||||||
kube::test::if_has_string "${output_message}" "/apis/batch/v1/namespaces/default/jobs 200 OK"
|
kube::test::if_has_string "${output_message}" "/apis/batch/v1/namespaces/default/jobs 200 OK"
|
||||||
kube::test::if_has_string "${output_message}" "/apis/extensions/v1beta1/namespaces/default/deployments 200 OK"
|
kube::test::if_has_string "${output_message}" "/apis/extensions/v1beta1/namespaces/default/deployments 200 OK"
|
||||||
|
@ -2749,7 +2749,7 @@ run_deployment_tests() {
|
||||||
output_message=$(kubectl get deployment.extensions -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
|
output_message=$(kubectl get deployment.extensions -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
|
||||||
kube::test::if_has_string "${output_message}" 'extensions/v1beta1'
|
kube::test::if_has_string "${output_message}" 'extensions/v1beta1'
|
||||||
output_message=$(kubectl get deployment.apps -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
|
output_message=$(kubectl get deployment.apps -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
|
||||||
kube::test::if_has_string "${output_message}" 'apps/v1beta1'
|
kube::test::if_has_string "${output_message}" 'apps/v1'
|
||||||
# Clean up
|
# Clean up
|
||||||
kubectl delete deployment test-nginx-extensions "${kube_flags[@]}"
|
kubectl delete deployment test-nginx-extensions "${kube_flags[@]}"
|
||||||
|
|
||||||
|
@ -2764,7 +2764,7 @@ run_deployment_tests() {
|
||||||
output_message=$(kubectl get deployment.extensions -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
|
output_message=$(kubectl get deployment.extensions -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
|
||||||
kube::test::if_has_string "${output_message}" 'extensions/v1beta1'
|
kube::test::if_has_string "${output_message}" 'extensions/v1beta1'
|
||||||
output_message=$(kubectl get deployment.apps -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
|
output_message=$(kubectl get deployment.apps -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
|
||||||
kube::test::if_has_string "${output_message}" 'apps/v1beta1'
|
kube::test::if_has_string "${output_message}" 'apps/v1'
|
||||||
# Describe command (resource only) should print detailed information
|
# Describe command (resource only) should print detailed information
|
||||||
kube::test::describe_resource_assert rs "Name:" "Pod Template:" "Labels:" "Selector:" "Controlled By" "Replicas:" "Pods Status:" "Volumes:"
|
kube::test::describe_resource_assert rs "Name:" "Pod Template:" "Labels:" "Selector:" "Controlled By" "Replicas:" "Pods Status:" "Volumes:"
|
||||||
# Describe command (resource only) should print detailed information
|
# Describe command (resource only) should print detailed information
|
||||||
|
|
|
@ -171,6 +171,7 @@ go_library(
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta1:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta1:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||||
|
|
|
@ -314,21 +314,19 @@ func (f *ring1Factory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ring1Factory) HistoryViewer(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) {
|
func (f *ring1Factory) HistoryViewer(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) {
|
||||||
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
external, err := f.clientAccessFactory.KubernetesClientSet()
|
||||||
clientset, err := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
|
return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), external)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ring1Factory) Rollbacker(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) {
|
func (f *ring1Factory) Rollbacker(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) {
|
||||||
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
external, err := f.clientAccessFactory.KubernetesClientSet()
|
||||||
clientset, err := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), clientset)
|
return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), external)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ring1Factory) StatusViewer(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) {
|
func (f *ring1Factory) StatusViewer(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) {
|
||||||
|
|
|
@ -27,18 +27,18 @@ import (
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/json"
|
"k8s.io/apimachinery/pkg/util/json"
|
||||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
clientappsv1beta1 "k8s.io/client-go/kubernetes/typed/apps/v1beta1"
|
clientappsv1beta1 "k8s.io/client-go/kubernetes/typed/apps/v1beta1"
|
||||||
clientextensionsv1beta1 "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
|
clientextv1beta1 "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
|
||||||
apiv1 "k8s.io/kubernetes/pkg/api/v1"
|
apiv1 "k8s.io/kubernetes/pkg/api/v1"
|
||||||
"k8s.io/kubernetes/pkg/apis/apps"
|
"k8s.io/kubernetes/pkg/apis/apps"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
|
||||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||||
sliceutil "k8s.io/kubernetes/pkg/kubectl/util/slice"
|
sliceutil "k8s.io/kubernetes/pkg/kubectl/util/slice"
|
||||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||||
|
@ -53,7 +53,7 @@ type HistoryViewer interface {
|
||||||
ViewHistory(namespace, name string, revision int64) (string, error)
|
ViewHistory(namespace, name string, revision int64) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HistoryViewerFor(kind schema.GroupKind, c clientset.Interface) (HistoryViewer, error) {
|
func HistoryViewerFor(kind schema.GroupKind, c kubernetes.Interface) (HistoryViewer, error) {
|
||||||
switch kind {
|
switch kind {
|
||||||
case extensions.Kind("Deployment"), apps.Kind("Deployment"):
|
case extensions.Kind("Deployment"), apps.Kind("Deployment"):
|
||||||
return &DeploymentHistoryViewer{c}, nil
|
return &DeploymentHistoryViewer{c}, nil
|
||||||
|
@ -66,13 +66,13 @@ func HistoryViewerFor(kind schema.GroupKind, c clientset.Interface) (HistoryView
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeploymentHistoryViewer struct {
|
type DeploymentHistoryViewer struct {
|
||||||
c clientset.Interface
|
c kubernetes.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
// ViewHistory returns a revision-to-replicaset map as the revision history of a deployment
|
// ViewHistory returns a revision-to-replicaset map as the revision history of a deployment
|
||||||
// TODO: this should be a describer
|
// TODO: this should be a describer
|
||||||
func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
||||||
versionedExtensionsClient := versionedExtensionsClientV1beta1(h.c)
|
versionedExtensionsClient := h.c.ExtensionsV1beta1()
|
||||||
deployment, err := versionedExtensionsClient.Deployments(namespace).Get(name, metav1.GetOptions{})
|
deployment, err := versionedExtensionsClient.Deployments(namespace).Get(name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to retrieve deployment %s: %v", name, err)
|
return "", fmt.Errorf("failed to retrieve deployment %s: %v", name, err)
|
||||||
|
@ -148,24 +148,21 @@ func printTemplate(template *v1.PodTemplateSpec) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DaemonSetHistoryViewer struct {
|
type DaemonSetHistoryViewer struct {
|
||||||
c clientset.Interface
|
c kubernetes.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
// ViewHistory returns a revision-to-history map as the revision history of a deployment
|
// ViewHistory returns a revision-to-history map as the revision history of a deployment
|
||||||
// TODO: this should be a describer
|
// TODO: this should be a describer
|
||||||
func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
||||||
versionedAppsClient := versionedAppsClientV1beta1(h.c)
|
ds, history, err := daemonSetHistory(h.c.ExtensionsV1beta1(), h.c.AppsV1beta1(), namespace, name)
|
||||||
versionedExtensionsClient := versionedExtensionsClientV1beta1(h.c)
|
|
||||||
versionedObj, allHistory, err := controlledHistories(versionedAppsClient, versionedExtensionsClient, namespace, name, "DaemonSet")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to find history controlled by DaemonSet %s: %v", name, err)
|
return "", err
|
||||||
}
|
}
|
||||||
historyInfo := make(map[int64]*appsv1beta1.ControllerRevision)
|
historyInfo := make(map[int64]*appsv1beta1.ControllerRevision)
|
||||||
for _, history := range allHistory {
|
for _, history := range history {
|
||||||
// TODO: for now we assume revisions don't overlap, we may need to handle it
|
// TODO: for now we assume revisions don't overlap, we may need to handle it
|
||||||
historyInfo[history.Revision] = history
|
historyInfo[history.Revision] = history
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(historyInfo) == 0 {
|
if len(historyInfo) == 0 {
|
||||||
return "No rollout history found.", nil
|
return "No rollout history found.", nil
|
||||||
}
|
}
|
||||||
|
@ -176,13 +173,7 @@ func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision in
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("unable to find the specified revision")
|
return "", fmt.Errorf("unable to find the specified revision")
|
||||||
}
|
}
|
||||||
|
dsOfHistory, err := applyDaemonSetHistory(ds, history)
|
||||||
versionedDS, ok := versionedObj.(*extensionsv1beta1.DaemonSet)
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("unexpected non-DaemonSet object returned: %v", versionedDS)
|
|
||||||
}
|
|
||||||
|
|
||||||
dsOfHistory, err := applyHistory(versionedDS, history)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to parse history %s", history.Name)
|
return "", fmt.Errorf("unable to parse history %s", history.Name)
|
||||||
}
|
}
|
||||||
|
@ -212,93 +203,46 @@ func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision in
|
||||||
}
|
}
|
||||||
|
|
||||||
type StatefulSetHistoryViewer struct {
|
type StatefulSetHistoryViewer struct {
|
||||||
c clientset.Interface
|
c kubernetes.Interface
|
||||||
}
|
|
||||||
|
|
||||||
func getOwner(revision apps.ControllerRevision) *metav1.OwnerReference {
|
|
||||||
ownerRefs := revision.GetOwnerReferences()
|
|
||||||
for i := range ownerRefs {
|
|
||||||
owner := &ownerRefs[i]
|
|
||||||
if owner.Controller != nil && *owner.Controller == true {
|
|
||||||
return owner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ViewHistory returns a list of the revision history of a statefulset
|
// ViewHistory returns a list of the revision history of a statefulset
|
||||||
// TODO: this should be a describer
|
// TODO: this should be a describer
|
||||||
// TODO: needs to implement detailed revision view
|
// TODO: needs to implement detailed revision view
|
||||||
func (h *StatefulSetHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
func (h *StatefulSetHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
||||||
|
_, history, err := statefulSetHistory(h.c.AppsV1beta1(), namespace, name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
sts, err := h.c.Apps().StatefulSets(namespace).Get(name, metav1.GetOptions{})
|
if len(history) <= 0 {
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to retrieve statefulset %s", err)
|
|
||||||
}
|
|
||||||
selector, err := metav1.LabelSelectorAsSelector(sts.Spec.Selector)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to retrieve statefulset history %s", err)
|
|
||||||
}
|
|
||||||
revisions, err := h.c.Apps().ControllerRevisions(namespace).List(metav1.ListOptions{LabelSelector: selector.String()})
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to retrieve statefulset history %s", err)
|
|
||||||
}
|
|
||||||
if len(revisions.Items) <= 0 {
|
|
||||||
return "No rollout history found.", nil
|
return "No rollout history found.", nil
|
||||||
}
|
}
|
||||||
revisionNumbers := make([]int64, len(revisions.Items))
|
revisions := make([]int64, len(history))
|
||||||
for i := range revisions.Items {
|
for _, revision := range history {
|
||||||
if owner := getOwner(revisions.Items[i]); owner != nil && owner.UID == sts.UID {
|
revisions = append(revisions, revision.Revision)
|
||||||
revisionNumbers[i] = revisions.Items[i].Revision
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sliceutil.SortInts64(revisionNumbers)
|
sliceutil.SortInts64(revisions)
|
||||||
|
|
||||||
return tabbedString(func(out io.Writer) error {
|
return tabbedString(func(out io.Writer) error {
|
||||||
fmt.Fprintf(out, "REVISION\n")
|
fmt.Fprintf(out, "REVISION\n")
|
||||||
for _, r := range revisionNumbers {
|
for _, r := range revisions {
|
||||||
fmt.Fprintf(out, "%d\n", r)
|
fmt.Fprintf(out, "%d\n", r)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// controlledHistories returns all ControllerRevisions controlled by the given API object
|
// controlledHistories returns all ControllerRevisions in namespace that selected by selector and owned by accessor
|
||||||
func controlledHistories(apps clientappsv1beta1.AppsV1beta1Interface, extensions clientextensionsv1beta1.ExtensionsV1beta1Interface, namespace, name, kind string) (runtime.Object, []*appsv1beta1.ControllerRevision, error) {
|
func controlledHistory(
|
||||||
var obj runtime.Object
|
apps clientappsv1beta1.AppsV1beta1Interface,
|
||||||
var labelSelector *metav1.LabelSelector
|
namespace string,
|
||||||
|
selector labels.Selector,
|
||||||
switch kind {
|
accessor metav1.Object) ([]*appsv1beta1.ControllerRevision, error) {
|
||||||
case "DaemonSet":
|
|
||||||
ds, err := extensions.DaemonSets(namespace).Get(name, metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to retrieve DaemonSet %s: %v", name, err)
|
|
||||||
}
|
|
||||||
labelSelector = ds.Spec.Selector
|
|
||||||
obj = ds
|
|
||||||
case "StatefulSet":
|
|
||||||
ss, err := apps.StatefulSets(namespace).Get(name, metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to retrieve StatefulSet %s: %v", name, err)
|
|
||||||
}
|
|
||||||
labelSelector = ss.Spec.Selector
|
|
||||||
obj = ss
|
|
||||||
default:
|
|
||||||
return nil, nil, fmt.Errorf("unsupported API object kind: %s", kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
var result []*appsv1beta1.ControllerRevision
|
var result []*appsv1beta1.ControllerRevision
|
||||||
selector, err := metav1.LabelSelectorAsSelector(labelSelector)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
historyList, err := apps.ControllerRevisions(namespace).List(metav1.ListOptions{LabelSelector: selector.String()})
|
historyList, err := apps.ControllerRevisions(namespace).List(metav1.ListOptions{LabelSelector: selector.String()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
|
||||||
accessor, err := meta.Accessor(obj)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to obtain accessor for %s named %s: %v", kind, name, err)
|
|
||||||
}
|
}
|
||||||
for i := range historyList.Items {
|
for i := range historyList.Items {
|
||||||
history := historyList.Items[i]
|
history := historyList.Items[i]
|
||||||
|
@ -307,16 +251,59 @@ func controlledHistories(apps clientappsv1beta1.AppsV1beta1Interface, extensions
|
||||||
result = append(result, &history)
|
result = append(result, &history)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return obj, result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyHistory returns a specific revision of DaemonSet by applying the given history to a copy of the given DaemonSet
|
// daemonSetHistory returns the DaemonSet named name in namespace and all ControllerRevisions in its history.
|
||||||
func applyHistory(ds *extensionsv1beta1.DaemonSet, history *appsv1beta1.ControllerRevision) (*extensionsv1beta1.DaemonSet, error) {
|
func daemonSetHistory(
|
||||||
obj, err := legacyscheme.Scheme.New(ds.GroupVersionKind())
|
ext clientextv1beta1.ExtensionsV1beta1Interface,
|
||||||
|
apps clientappsv1beta1.AppsV1beta1Interface,
|
||||||
|
namespace, name string) (*extensionsv1beta1.DaemonSet, []*appsv1beta1.ControllerRevision, error) {
|
||||||
|
ds, err := ext.DaemonSets(namespace).Get(name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, fmt.Errorf("failed to retrieve DaemonSet %s: %v", name, err)
|
||||||
}
|
}
|
||||||
clone := obj.(*extensionsv1beta1.DaemonSet)
|
selector, err := metav1.LabelSelectorAsSelector(ds.Spec.Selector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to create selector for DaemonSet %s: %v", ds.Name, err)
|
||||||
|
}
|
||||||
|
accessor, err := meta.Accessor(ds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to create accessor for DaemonSet %s: %v", ds.Name, err)
|
||||||
|
}
|
||||||
|
history, err := controlledHistory(apps, ds.Namespace, selector, accessor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to find history controlled by DaemonSet %s: %v", ds.Name, err)
|
||||||
|
}
|
||||||
|
return ds, history, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// statefulSetHistory returns the StatefulSet named name in namespace and all ControllerRevisions in its history.
|
||||||
|
func statefulSetHistory(
|
||||||
|
apps clientappsv1beta1.AppsV1beta1Interface,
|
||||||
|
namespace, name string) (*appsv1beta1.StatefulSet, []*appsv1beta1.ControllerRevision, error) {
|
||||||
|
sts, err := apps.StatefulSets(namespace).Get(name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to retrieve Statefulset %s: %s", name, err.Error())
|
||||||
|
}
|
||||||
|
selector, err := metav1.LabelSelectorAsSelector(sts.Spec.Selector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to create selector for StatefulSet %s: %s", name, err.Error())
|
||||||
|
}
|
||||||
|
accessor, err := meta.Accessor(sts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to obtain accessor for StatefulSet %s: %s", name, err.Error())
|
||||||
|
}
|
||||||
|
history, err := controlledHistory(apps, namespace, selector, accessor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to find history controlled by StatefulSet %s: %v", name, err)
|
||||||
|
}
|
||||||
|
return sts, history, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyDaemonSetHistory returns a specific revision of DaemonSet by applying the given history to a copy of the given DaemonSet
|
||||||
|
func applyDaemonSetHistory(ds *extensionsv1beta1.DaemonSet, history *appsv1beta1.ControllerRevision) (*extensionsv1beta1.DaemonSet, error) {
|
||||||
|
clone := ds.DeepCopy()
|
||||||
cloneBytes, err := json.Marshal(clone)
|
cloneBytes, err := json.Marshal(clone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -26,18 +26,19 @@ import (
|
||||||
|
|
||||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
externalextensions "k8s.io/api/extensions/v1beta1"
|
extv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
apiv1 "k8s.io/kubernetes/pkg/api/v1"
|
apiv1 "k8s.io/kubernetes/pkg/api/v1"
|
||||||
"k8s.io/kubernetes/pkg/apis/apps"
|
"k8s.io/kubernetes/pkg/apis/apps"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
|
||||||
"k8s.io/kubernetes/pkg/controller/daemon"
|
"k8s.io/kubernetes/pkg/controller/daemon"
|
||||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||||
"k8s.io/kubernetes/pkg/controller/statefulset"
|
"k8s.io/kubernetes/pkg/controller/statefulset"
|
||||||
|
@ -55,7 +56,7 @@ type Rollbacker interface {
|
||||||
Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error)
|
Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RollbackerFor(kind schema.GroupKind, c clientset.Interface) (Rollbacker, error) {
|
func RollbackerFor(kind schema.GroupKind, c kubernetes.Interface) (Rollbacker, error) {
|
||||||
switch kind {
|
switch kind {
|
||||||
case extensions.Kind("Deployment"), apps.Kind("Deployment"):
|
case extensions.Kind("Deployment"), apps.Kind("Deployment"):
|
||||||
return &DeploymentRollbacker{c}, nil
|
return &DeploymentRollbacker{c}, nil
|
||||||
|
@ -68,7 +69,7 @@ func RollbackerFor(kind schema.GroupKind, c clientset.Interface) (Rollbacker, er
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeploymentRollbacker struct {
|
type DeploymentRollbacker struct {
|
||||||
c clientset.Interface
|
c kubernetes.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error) {
|
func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error) {
|
||||||
|
@ -82,26 +83,26 @@ func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations m
|
||||||
if d.Spec.Paused {
|
if d.Spec.Paused {
|
||||||
return "", fmt.Errorf("you cannot rollback a paused deployment; resume it first with 'kubectl rollout resume deployment/%s' and try again", d.Name)
|
return "", fmt.Errorf("you cannot rollback a paused deployment; resume it first with 'kubectl rollout resume deployment/%s' and try again", d.Name)
|
||||||
}
|
}
|
||||||
deploymentRollback := &extensions.DeploymentRollback{
|
deploymentRollback := &extv1beta1.DeploymentRollback{
|
||||||
Name: d.Name,
|
Name: d.Name,
|
||||||
UpdatedAnnotations: updatedAnnotations,
|
UpdatedAnnotations: updatedAnnotations,
|
||||||
RollbackTo: extensions.RollbackConfig{
|
RollbackTo: extv1beta1.RollbackConfig{
|
||||||
Revision: toRevision,
|
Revision: toRevision,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result := ""
|
result := ""
|
||||||
|
|
||||||
// Get current events
|
// Get current events
|
||||||
events, err := r.c.Core().Events(d.Namespace).List(metav1.ListOptions{})
|
events, err := r.c.CoreV1().Events(d.Namespace).List(metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
// Do the rollback
|
// Do the rollback
|
||||||
if err := r.c.Extensions().Deployments(d.Namespace).Rollback(deploymentRollback); err != nil {
|
if err := r.c.ExtensionsV1beta1().Deployments(d.Namespace).Rollback(deploymentRollback); err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
// Watch for the changes of events
|
// Watch for the changes of events
|
||||||
watch, err := r.c.Core().Events(d.Namespace).Watch(metav1.ListOptions{Watch: true, ResourceVersion: events.ResourceVersion})
|
watch, err := r.c.CoreV1().Events(d.Namespace).Watch(metav1.ListOptions{Watch: true, ResourceVersion: events.ResourceVersion})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
@ -150,13 +151,13 @@ func isRollbackEvent(e *api.Event) (bool, string) {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func simpleDryRun(deployment *extensions.Deployment, c clientset.Interface, toRevision int64) (string, error) {
|
func simpleDryRun(deployment *extensions.Deployment, c kubernetes.Interface, toRevision int64) (string, error) {
|
||||||
externalDeployment := &externalextensions.Deployment{}
|
externalDeployment := &extv1beta1.Deployment{}
|
||||||
if err := legacyscheme.Scheme.Convert(deployment, externalDeployment, nil); err != nil {
|
if err := legacyscheme.Scheme.Convert(deployment, externalDeployment, nil); err != nil {
|
||||||
return "", fmt.Errorf("failed to convert deployment, %v", err)
|
return "", fmt.Errorf("failed to convert deployment, %v", err)
|
||||||
}
|
}
|
||||||
versionedExtensionsClient := versionedExtensionsClientV1beta1(c)
|
|
||||||
_, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(externalDeployment, versionedExtensionsClient)
|
_, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(externalDeployment, c.ExtensionsV1beta1())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", deployment.Name, err)
|
return "", fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", deployment.Name, err)
|
||||||
}
|
}
|
||||||
|
@ -213,40 +214,32 @@ func simpleDryRun(deployment *extensions.Deployment, c clientset.Interface, toRe
|
||||||
}
|
}
|
||||||
|
|
||||||
type DaemonSetRollbacker struct {
|
type DaemonSetRollbacker struct {
|
||||||
c clientset.Interface
|
c kubernetes.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error) {
|
func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error) {
|
||||||
if toRevision < 0 {
|
if toRevision < 0 {
|
||||||
return "", revisionNotFoundErr(toRevision)
|
return "", revisionNotFoundErr(toRevision)
|
||||||
}
|
}
|
||||||
|
accessor, err := meta.Accessor(obj)
|
||||||
ds, ok := obj.(*extensions.DaemonSet)
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("passed object is not a DaemonSet: %#v", obj)
|
|
||||||
}
|
|
||||||
versionedAppsClient := versionedAppsClientV1beta1(r.c)
|
|
||||||
versionedExtensionsClient := versionedExtensionsClientV1beta1(r.c)
|
|
||||||
versionedObj, allHistory, err := controlledHistories(versionedAppsClient, versionedExtensionsClient, ds.Namespace, ds.Name, "DaemonSet")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to find history controlled by DaemonSet %s: %v", ds.Name, err)
|
return "", fmt.Errorf("failed to create accessor for kind %v: %s", obj.GetObjectKind(), err.Error())
|
||||||
}
|
}
|
||||||
versionedDS, ok := versionedObj.(*externalextensions.DaemonSet)
|
ds, history, err := daemonSetHistory(r.c.ExtensionsV1beta1(), r.c.AppsV1beta1(), accessor.GetNamespace(), accessor.GetName())
|
||||||
if !ok {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unexpected non-DaemonSet object returned: %v", versionedDS)
|
return "", err
|
||||||
}
|
}
|
||||||
|
if toRevision == 0 && len(history) <= 1 {
|
||||||
if toRevision == 0 && len(allHistory) <= 1 {
|
|
||||||
return "", fmt.Errorf("no last revision to roll back to")
|
return "", fmt.Errorf("no last revision to roll back to")
|
||||||
}
|
}
|
||||||
|
|
||||||
toHistory := findHistory(toRevision, allHistory)
|
toHistory := findHistory(toRevision, history)
|
||||||
if toHistory == nil {
|
if toHistory == nil {
|
||||||
return "", revisionNotFoundErr(toRevision)
|
return "", revisionNotFoundErr(toRevision)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dryRun {
|
if dryRun {
|
||||||
appliedDS, err := applyHistory(versionedDS, toHistory)
|
appliedDS, err := applyDaemonSetHistory(ds, toHistory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -254,7 +247,7 @@ func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations ma
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if the revision already matches current DaemonSet
|
// Skip if the revision already matches current DaemonSet
|
||||||
done, err := daemon.Match(versionedDS, toHistory)
|
done, err := daemon.Match(ds, toHistory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -263,7 +256,7 @@ func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations ma
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore revision
|
// Restore revision
|
||||||
if _, err = versionedExtensionsClient.DaemonSets(ds.Namespace).Patch(ds.Name, types.StrategicMergePatchType, toHistory.Data.Raw); err != nil {
|
if _, err = r.c.ExtensionsV1beta1().DaemonSets(accessor.GetNamespace()).Patch(accessor.GetName(), types.StrategicMergePatchType, toHistory.Data.Raw); err != nil {
|
||||||
return "", fmt.Errorf("failed restoring revision %d: %v", toRevision, err)
|
return "", fmt.Errorf("failed restoring revision %d: %v", toRevision, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +264,7 @@ func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations ma
|
||||||
}
|
}
|
||||||
|
|
||||||
type StatefulSetRollbacker struct {
|
type StatefulSetRollbacker struct {
|
||||||
c clientset.Interface
|
c kubernetes.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
// toRevision is a non-negative integer, with 0 being reserved to indicate rolling back to previous configuration
|
// toRevision is a non-negative integer, with 0 being reserved to indicate rolling back to previous configuration
|
||||||
|
@ -279,34 +272,25 @@ func (r *StatefulSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations
|
||||||
if toRevision < 0 {
|
if toRevision < 0 {
|
||||||
return "", revisionNotFoundErr(toRevision)
|
return "", revisionNotFoundErr(toRevision)
|
||||||
}
|
}
|
||||||
|
accessor, err := meta.Accessor(obj)
|
||||||
ss, ok := obj.(*apps.StatefulSet)
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("passed object is not a StatefulSet: %#v", obj)
|
|
||||||
}
|
|
||||||
versionedAppsClient := versionedAppsClientV1beta1(r.c)
|
|
||||||
versionedExtensionsClient := versionedExtensionsClientV1beta1(r.c)
|
|
||||||
versionedObj, allHistory, err := controlledHistories(versionedAppsClient, versionedExtensionsClient, ss.Namespace, ss.Name, "StatefulSet")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to find history controlled by StatefulSet %s: %v", ss.Name, err)
|
return "", fmt.Errorf("failed to create accessor for kind %v: %s", obj.GetObjectKind(), err.Error())
|
||||||
}
|
}
|
||||||
|
sts, history, err := statefulSetHistory(r.c.AppsV1beta1(), accessor.GetNamespace(), accessor.GetName())
|
||||||
versionedSS, ok := versionedObj.(*appsv1beta1.StatefulSet)
|
if err != nil {
|
||||||
if !ok {
|
return "", err
|
||||||
return "", fmt.Errorf("unexpected non-StatefulSet object returned: %v", versionedSS)
|
|
||||||
}
|
}
|
||||||
|
if toRevision == 0 && len(history) <= 1 {
|
||||||
if toRevision == 0 && len(allHistory) <= 1 {
|
|
||||||
return "", fmt.Errorf("no last revision to roll back to")
|
return "", fmt.Errorf("no last revision to roll back to")
|
||||||
}
|
}
|
||||||
|
|
||||||
toHistory := findHistory(toRevision, allHistory)
|
toHistory := findHistory(toRevision, history)
|
||||||
if toHistory == nil {
|
if toHistory == nil {
|
||||||
return "", revisionNotFoundErr(toRevision)
|
return "", revisionNotFoundErr(toRevision)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dryRun {
|
if dryRun {
|
||||||
appliedSS, err := statefulset.ApplyRevision(versionedSS, toHistory)
|
appliedSS, err := statefulset.ApplyRevision(sts, toHistory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -314,7 +298,7 @@ func (r *StatefulSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if the revision already matches current StatefulSet
|
// Skip if the revision already matches current StatefulSet
|
||||||
done, err := statefulset.Match(versionedSS, toHistory)
|
done, err := statefulset.Match(sts, toHistory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -323,7 +307,7 @@ func (r *StatefulSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore revision
|
// Restore revision
|
||||||
if _, err = versionedAppsClient.StatefulSets(ss.Namespace).Patch(ss.Name, types.StrategicMergePatchType, toHistory.Data.Raw); err != nil {
|
if _, err = r.c.AppsV1beta1().StatefulSets(sts.Namespace).Patch(sts.Name, types.StrategicMergePatchType, toHistory.Data.Raw); err != nil {
|
||||||
return "", fmt.Errorf("failed restoring revision %d: %v", toRevision, err)
|
return "", fmt.Errorf("failed restoring revision %d: %v", toRevision, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue