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/core/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/statefulset:go_default_library",
|
||||
"//pkg/credentialprovider:go_default_library",
|
||||
"//pkg/kubectl/apps: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/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
apiv1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
||||
"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"
|
||||
sliceutil "k8s.io/kubernetes/pkg/kubectl/util/slice"
|
||||
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 (
|
||||
|
@ -278,7 +279,7 @@ func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations ma
|
|||
}
|
||||
|
||||
// Skip if the revision already matches current DaemonSet
|
||||
done, err := daemon.Match(ds, toHistory)
|
||||
done, err := daemonSetMatch(ds, toHistory)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -294,6 +295,42 @@ func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations ma
|
|||
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 {
|
||||
c kubernetes.Interface
|
||||
}
|
||||
|
@ -321,7 +358,7 @@ func (r *StatefulSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations
|
|||
}
|
||||
|
||||
if dryRun {
|
||||
appliedSS, err := statefulset.ApplyRevision(sts, toHistory)
|
||||
appliedSS, err := applyRevision(sts, toHistory)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -329,7 +366,7 @@ func (r *StatefulSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations
|
|||
}
|
||||
|
||||
// Skip if the revision already matches current StatefulSet
|
||||
done, err := statefulset.Match(sts, toHistory)
|
||||
done, err := statefulsetMatch(sts, toHistory)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -345,6 +382,54 @@ func (r *StatefulSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations
|
|||
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.
|
||||
// It returns nil if no such controllerrevision exists.
|
||||
// If toRevision is 0, the last previously used history is returned.
|
||||
|
|
Loading…
Reference in New Issue