mirror of https://github.com/k3s-io/k3s
kubectl should not have a direct code dependency on controllers
The dependency linkage brings in the scheduler and a lot of unrelated code. Instead, selectively copy methods that operate on public APIs.pull/8/head
parent
3dbcd1ddce
commit
1a1e8344a5
|
@ -132,9 +132,7 @@ go_library(
|
||||||
"//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library",
|
"//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library",
|
||||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||||
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
|
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
|
||||||
"//pkg/controller/daemon:go_default_library",
|
|
||||||
"//pkg/controller/deployment/util:go_default_library",
|
"//pkg/controller/deployment/util:go_default_library",
|
||||||
"//pkg/controller/statefulset:go_default_library",
|
|
||||||
"//pkg/credentialprovider:go_default_library",
|
"//pkg/credentialprovider:go_default_library",
|
||||||
"//pkg/kubectl/apps:go_default_library",
|
"//pkg/kubectl/apps:go_default_library",
|
||||||
"//pkg/kubectl/cmd/scalejob:go_default_library",
|
"//pkg/kubectl/cmd/scalejob:go_default_library",
|
||||||
|
|
|
@ -32,18 +32,19 @@ import (
|
||||||
"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/util/json"
|
||||||
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
apiv1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
apiv1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/controller/daemon"
|
|
||||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
|
||||||
"k8s.io/kubernetes/pkg/controller/statefulset"
|
|
||||||
kapps "k8s.io/kubernetes/pkg/kubectl/apps"
|
kapps "k8s.io/kubernetes/pkg/kubectl/apps"
|
||||||
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"
|
||||||
|
// kubectl should not be taking dependencies on logic in the controllers
|
||||||
|
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -278,7 +279,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(ds, toHistory)
|
done, err := daemonSetMatch(ds, toHistory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -294,6 +295,42 @@ func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations ma
|
||||||
return rollbackSuccess, nil
|
return rollbackSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// daemonMatch check if the given DaemonSet's template matches the template stored in the given history.
|
||||||
|
func daemonSetMatch(ds *appsv1.DaemonSet, history *appsv1.ControllerRevision) (bool, error) {
|
||||||
|
patch, err := getDaemonSetPatch(ds)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return bytes.Equal(patch, history.Data.Raw), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPatch returns a strategic merge patch that can be applied to restore a Daemonset to a
|
||||||
|
// previous version. If the returned error is nil the patch is valid. The current state that we save is just the
|
||||||
|
// PodSpecTemplate. We can modify this later to encompass more state (or less) and remain compatible with previously
|
||||||
|
// recorded patches.
|
||||||
|
func getDaemonSetPatch(ds *appsv1.DaemonSet) ([]byte, error) {
|
||||||
|
dsBytes, err := json.Marshal(ds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var raw map[string]interface{}
|
||||||
|
err = json.Unmarshal(dsBytes, &raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
objCopy := make(map[string]interface{})
|
||||||
|
specCopy := make(map[string]interface{})
|
||||||
|
|
||||||
|
// Create a patch of the DaemonSet that replaces spec.template
|
||||||
|
spec := raw["spec"].(map[string]interface{})
|
||||||
|
template := spec["template"].(map[string]interface{})
|
||||||
|
specCopy["template"] = template
|
||||||
|
template["$patch"] = "replace"
|
||||||
|
objCopy["spec"] = specCopy
|
||||||
|
patch, err := json.Marshal(objCopy)
|
||||||
|
return patch, err
|
||||||
|
}
|
||||||
|
|
||||||
type StatefulSetRollbacker struct {
|
type StatefulSetRollbacker struct {
|
||||||
c kubernetes.Interface
|
c kubernetes.Interface
|
||||||
}
|
}
|
||||||
|
@ -321,7 +358,7 @@ func (r *StatefulSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations
|
||||||
}
|
}
|
||||||
|
|
||||||
if dryRun {
|
if dryRun {
|
||||||
appliedSS, err := statefulset.ApplyRevision(sts, toHistory)
|
appliedSS, err := applyRevision(sts, toHistory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -329,7 +366,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(sts, toHistory)
|
done, err := statefulsetMatch(sts, toHistory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -345,6 +382,54 @@ func (r *StatefulSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations
|
||||||
return rollbackSuccess, nil
|
return rollbackSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var appsCodec = legacyscheme.Codecs.LegacyCodec(appsv1.SchemeGroupVersion)
|
||||||
|
|
||||||
|
// applyRevision returns a new StatefulSet constructed by restoring the state in revision to set. If the returned error
|
||||||
|
// is nil, the returned StatefulSet is valid.
|
||||||
|
func applyRevision(set *appsv1.StatefulSet, revision *appsv1.ControllerRevision) (*appsv1.StatefulSet, error) {
|
||||||
|
clone := set.DeepCopy()
|
||||||
|
patched, err := strategicpatch.StrategicMergePatch([]byte(runtime.EncodeOrDie(appsCodec, clone)), revision.Data.Raw, clone)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(patched, clone)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return clone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// statefulsetMatch check if the given StatefulSet's template matches the template stored in the given history.
|
||||||
|
func statefulsetMatch(ss *appsv1.StatefulSet, history *appsv1.ControllerRevision) (bool, error) {
|
||||||
|
patch, err := getStatefulSetPatch(ss)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return bytes.Equal(patch, history.Data.Raw), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getStatefulSetPatch returns a strategic merge patch that can be applied to restore a StatefulSet to a
|
||||||
|
// previous version. If the returned error is nil the patch is valid. The current state that we save is just the
|
||||||
|
// PodSpecTemplate. We can modify this later to encompass more state (or less) and remain compatible with previously
|
||||||
|
// recorded patches.
|
||||||
|
func getStatefulSetPatch(set *appsv1.StatefulSet) ([]byte, error) {
|
||||||
|
str, err := runtime.Encode(appsCodec, set)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var raw map[string]interface{}
|
||||||
|
json.Unmarshal([]byte(str), &raw)
|
||||||
|
objCopy := make(map[string]interface{})
|
||||||
|
specCopy := make(map[string]interface{})
|
||||||
|
spec := raw["spec"].(map[string]interface{})
|
||||||
|
template := spec["template"].(map[string]interface{})
|
||||||
|
specCopy["template"] = template
|
||||||
|
template["$patch"] = "replace"
|
||||||
|
objCopy["spec"] = specCopy
|
||||||
|
patch, err := json.Marshal(objCopy)
|
||||||
|
return patch, err
|
||||||
|
}
|
||||||
|
|
||||||
// findHistory returns a controllerrevision of a specific revision from the given controllerrevisions.
|
// findHistory returns a controllerrevision of a specific revision from the given controllerrevisions.
|
||||||
// It returns nil if no such controllerrevision exists.
|
// It returns nil if no such controllerrevision exists.
|
||||||
// If toRevision is 0, the last previously used history is returned.
|
// If toRevision is 0, the last previously used history is returned.
|
||||||
|
|
Loading…
Reference in New Issue