mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 10:51:29 +00:00
diff: Fix broken Local()
logic
Local and Live functions where doing and returning the same thing, giving empty results by default. Fix the local function by copying the objects before fetching the live version.
This commit is contained in:
parent
bd0d093701
commit
f2b48a90df
@ -1185,6 +1185,39 @@ run_kubectl_apply_deployments_tests() {
|
||||
set +o errexit
|
||||
}
|
||||
|
||||
# Runs tests for kubectl alpha diff
|
||||
run_kubectl_diff_tests() {
|
||||
set -o nounset
|
||||
set -o errexit
|
||||
|
||||
create_and_use_new_namespace
|
||||
kube::log::status "Testing kubectl alpha diff"
|
||||
|
||||
kubectl apply -f hack/testdata/pod.yaml
|
||||
|
||||
# Ensure that selfLink has been added, and shown in the diff
|
||||
output_message=$(kubectl alpha diff -f hack/testdata/pod.yaml)
|
||||
kube::test::if_has_string "${output_message}" 'selfLink'
|
||||
output_message=$(kubectl alpha diff LOCAL LIVE -f hack/testdata/pod.yaml)
|
||||
kube::test::if_has_string "${output_message}" 'selfLink'
|
||||
output_message=$(kubectl alpha diff LOCAL MERGED -f hack/testdata/pod.yaml)
|
||||
kube::test::if_has_string "${output_message}" 'selfLink'
|
||||
|
||||
output_message=$(kubectl alpha diff MERGED MERGED -f hack/testdata/pod.yaml)
|
||||
kube::test::if_empty_string "${output_message}"
|
||||
output_message=$(kubectl alpha diff LIVE LIVE -f hack/testdata/pod.yaml)
|
||||
kube::test::if_empty_string "${output_message}"
|
||||
output_message=$(kubectl alpha diff LAST LAST -f hack/testdata/pod.yaml)
|
||||
kube::test::if_empty_string "${output_message}"
|
||||
output_message=$(kubectl alpha diff LOCAL LOCAL -f hack/testdata/pod.yaml)
|
||||
kube::test::if_empty_string "${output_message}"
|
||||
|
||||
kubectl delete -f hack/testdata/pod.yaml
|
||||
|
||||
set +o nounset
|
||||
set +o errexit
|
||||
}
|
||||
|
||||
# Runs tests for --save-config tests.
|
||||
run_save_config_tests() {
|
||||
set -o nounset
|
||||
@ -4983,6 +5016,11 @@ runTests() {
|
||||
record_command run_kubectl_apply_deployments_tests
|
||||
fi
|
||||
|
||||
################
|
||||
# Kubectl diff #
|
||||
################
|
||||
record_command run_kubectl_diff_tests
|
||||
|
||||
###############
|
||||
# Kubectl get #
|
||||
###############
|
||||
|
@ -26,9 +26,11 @@ import (
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/dynamic"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/parse"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
@ -263,6 +265,7 @@ type Object interface {
|
||||
// InfoObject is an implementation of the Object interface. It gets all
|
||||
// the information from the Info object.
|
||||
type InfoObject struct {
|
||||
Remote runtime.Unstructured
|
||||
Info *resource.Info
|
||||
Encoder runtime.Encoder
|
||||
Parser *parse.Factory
|
||||
@ -288,14 +291,10 @@ func (obj InfoObject) Local() (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
func (obj InfoObject) Live() (map[string]interface{}, error) {
|
||||
if obj.Info.Object == nil {
|
||||
if obj.Remote == nil {
|
||||
return nil, nil // Object doesn't exist on cluster.
|
||||
}
|
||||
data, err := runtime.Encode(obj.Encoder, obj.Info.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.toMap(data)
|
||||
return obj.Remote.UnstructuredContent(), nil
|
||||
}
|
||||
|
||||
func (obj InfoObject) Merged() (map[string]interface{}, error) {
|
||||
@ -327,10 +326,10 @@ func (obj InfoObject) Merged() (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
func (obj InfoObject) Last() (map[string]interface{}, error) {
|
||||
if obj.Info.Object == nil {
|
||||
if obj.Remote == nil {
|
||||
return nil, nil // No object is live, return empty
|
||||
}
|
||||
accessor, err := meta.Accessor(obj.Info.Object)
|
||||
accessor, err := meta.Accessor(obj.Remote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -390,6 +389,50 @@ func (d *Differ) TearDown() {
|
||||
d.To.Dir.Delete() // Ignore error
|
||||
}
|
||||
|
||||
type Downloader struct {
|
||||
mapper meta.RESTMapper
|
||||
dclient dynamic.Interface
|
||||
ns string
|
||||
}
|
||||
|
||||
func NewDownloader(f cmdutil.Factory) (*Downloader, error) {
|
||||
var err error
|
||||
var d Downloader
|
||||
|
||||
d.mapper, err = f.RESTMapper()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.dclient, err = f.DynamicClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.ns, _, _ = f.DefaultNamespace()
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
func (d *Downloader) Download(info *resource.Info) (*unstructured.Unstructured, error) {
|
||||
gvk := info.Object.GetObjectKind().GroupVersionKind()
|
||||
mapping, err := d.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resource dynamic.ResourceInterface
|
||||
switch mapping.Scope.Name() {
|
||||
case meta.RESTScopeNameNamespace:
|
||||
if info.Namespace == "" {
|
||||
info.Namespace = d.ns
|
||||
}
|
||||
resource = d.dclient.Resource(mapping.Resource).Namespace(info.Namespace)
|
||||
case meta.RESTScopeNameRoot:
|
||||
resource = d.dclient.Resource(mapping.Resource)
|
||||
}
|
||||
|
||||
return resource.Get(info.Name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// RunDiff uses the factory to parse file arguments, find the version to
|
||||
// diff, and find each Info object for each files, and runs against the
|
||||
// differ.
|
||||
@ -417,25 +460,26 @@ func RunDiff(f cmdutil.Factory, diff *DiffProgram, options *DiffOptions, from, t
|
||||
Unstructured().
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||
FilenameParam(enforceNamespace, &options.FilenameOptions).
|
||||
Local().
|
||||
Flatten().
|
||||
Do()
|
||||
if err := r.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dl, err := NewDownloader(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := info.Get(); err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%s\nfrom server for:", info.String()), info.Source, err)
|
||||
}
|
||||
info.Object = nil
|
||||
}
|
||||
|
||||
remote, _ := dl.Download(info)
|
||||
obj := InfoObject{
|
||||
Remote: remote,
|
||||
Info: info,
|
||||
Parser: parser,
|
||||
Encoder: cmdutil.InternalVersionJSONEncoder(),
|
||||
|
Loading…
Reference in New Issue
Block a user