Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
e5964b3f
Unverified
Commit
e5964b3f
authored
Sep 27, 2018
by
k8s-ci-robot
Committed by
GitHub
Sep 27, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #69162 from apelisse/kubectl-dry-run-diff
Use dry-run patch to get the merged version of the object
parents
3c43f089
2d3f7795
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
67 additions
and
132 deletions
+67
-132
BUILD
pkg/kubectl/cmd/BUILD
+0
-2
diff.go
pkg/kubectl/cmd/diff.go
+59
-124
diff_test.go
pkg/kubectl/cmd/diff_test.go
+8
-6
No files found.
pkg/kubectl/cmd/BUILD
View file @
e5964b3f
...
...
@@ -60,8 +60,6 @@ go_library(
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/apply/parse:go_default_library",
"//pkg/kubectl/apply/strategy:go_default_library",
"//pkg/kubectl/cmd/auth:go_default_library",
"//pkg/kubectl/cmd/config:go_default_library",
"//pkg/kubectl/cmd/create:go_default_library",
...
...
pkg/kubectl/cmd/diff.go
View file @
e5964b3f
...
...
@@ -17,7 +17,6 @@ limitations under the License.
package
cmd
import
(
"encoding/json"
"fmt"
"io"
"io/ioutil"
...
...
@@ -25,19 +24,20 @@ import (
"path/filepath"
"github.com/ghodss/yaml"
"github.com/jonboulle/clockwork"
"github.com/spf13/cobra"
corev1
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/
meta
"
"k8s.io/apimachinery/pkg/api/
errors
"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
"k8s.io/client-go/dynamic"
"k8s.io/kubernetes/pkg/kubectl/apply/parse"
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil
"k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/utils/exec"
)
...
...
@@ -130,7 +130,7 @@ func (d *DiffProgram) Run(from, to string) error {
type
Printer
struct
{}
// Print the object inside the writer w.
func
(
p
*
Printer
)
Print
(
obj
map
[
string
]
interface
{}
,
w
io
.
Writer
)
error
{
func
(
p
*
Printer
)
Print
(
obj
runtime
.
Object
,
w
io
.
Writer
)
error
{
if
obj
==
nil
{
return
nil
}
...
...
@@ -161,10 +161,10 @@ func NewDiffVersion(name string) (*DiffVersion, error) {
},
nil
}
func
(
v
*
DiffVersion
)
getObject
(
obj
Object
)
(
map
[
string
]
interface
{}
,
error
)
{
func
(
v
*
DiffVersion
)
getObject
(
obj
Object
)
(
runtime
.
Object
,
error
)
{
switch
v
.
Name
{
case
"LIVE"
:
return
obj
.
Live
()
return
obj
.
Live
()
,
nil
case
"MERGED"
:
return
obj
.
Merged
()
}
...
...
@@ -216,8 +216,8 @@ func (d *Directory) Delete() error {
// Object is an interface that let's you retrieve multiple version of
// it.
type
Object
interface
{
Live
()
(
map
[
string
]
interface
{},
error
)
Merged
()
(
map
[
string
]
interface
{}
,
error
)
Live
()
runtime
.
Object
Merged
()
(
runtime
.
Object
,
error
)
Name
()
string
}
...
...
@@ -225,73 +225,51 @@ type Object interface {
// InfoObject is an implementation of the Object interface. It gets all
// the information from the Info object.
type
InfoObject
struct
{
Remote
*
unstructured
.
Unstructured
Info
*
resource
.
Info
Encoder
runtime
.
Encoder
Parser
*
parse
.
Factory
LocalObj
runtime
.
Object
Info
*
resource
.
Info
Encoder
runtime
.
Encoder
OpenAPI
openapi
.
Resources
}
var
_
Object
=
&
InfoObject
{}
func
(
obj
InfoObject
)
toMap
(
data
[]
byte
)
(
map
[
string
]
interface
{},
error
)
{
m
:=
map
[
string
]
interface
{}{}
if
len
(
data
)
==
0
{
return
m
,
nil
}
err
:=
json
.
Unmarshal
(
data
,
&
m
)
return
m
,
err
// Returns the live version of the object
func
(
obj
InfoObject
)
Live
()
runtime
.
Object
{
return
obj
.
Info
.
Object
}
func
(
obj
InfoObject
)
Live
()
(
map
[
string
]
interface
{},
error
)
{
if
obj
.
Remote
==
nil
{
return
nil
,
nil
// Object doesn't exist on cluster.
}
return
obj
.
Remote
.
UnstructuredContent
(),
nil
}
func
(
obj
InfoObject
)
Merged
()
(
map
[
string
]
interface
{},
error
)
{
data
,
err
:=
runtime
.
Encode
(
obj
.
Encoder
,
obj
.
Info
.
Object
)
if
err
!=
nil
{
return
nil
,
err
}
local
,
err
:=
obj
.
toMap
(
data
)
live
,
err
:=
obj
.
Live
()
if
err
!=
nil
{
return
nil
,
err
}
last
,
err
:=
obj
.
Last
()
// Returns the "merged" object, as it would look like if applied or
// created.
func
(
obj
InfoObject
)
Merged
()
(
runtime
.
Object
,
error
)
{
// Build the patcher, and then apply the patch with dry-run, unless the object doesn't exist, in which case we need to create it.
if
obj
.
Live
()
==
nil
{
// Dry-run create if the object doesn't exist.
return
resource
.
NewHelper
(
obj
.
Info
.
Client
,
obj
.
Info
.
Mapping
)
.
Create
(
obj
.
Info
.
Namespace
,
true
,
obj
.
LocalObj
,
&
metav1
.
CreateOptions
{
DryRun
:
[]
string
{
metav1
.
DryRunAll
}},
)
}
modified
,
err
:=
kubectl
.
GetModifiedConfiguration
(
obj
.
LocalObj
,
false
,
unstructured
.
UnstructuredJSONScheme
)
if
err
!=
nil
{
return
nil
,
err
}
if
live
==
nil
||
last
==
nil
{
return
local
,
nil
// We probably don't have a live version, merged is local.
// This is using the patcher from apply, to keep the same behavior.
// We plan on replacing this with server-side apply when it becomes available.
patcher
:=
&
patcher
{
mapping
:
obj
.
Info
.
Mapping
,
helper
:
resource
.
NewHelper
(
obj
.
Info
.
Client
,
obj
.
Info
.
Mapping
),
overwrite
:
true
,
backOff
:
clockwork
.
NewRealClock
(),
serverDryRun
:
true
,
openapiSchema
:
obj
.
OpenAPI
,
}
elmt
,
err
:=
obj
.
Parser
.
CreateElement
(
last
,
local
,
live
)
if
err
!=
nil
{
return
nil
,
err
}
result
,
err
:=
elmt
.
Merge
(
strategy
.
Create
(
strategy
.
Options
{}))
return
result
.
MergedResult
.
(
map
[
string
]
interface
{}),
err
}
func
(
obj
InfoObject
)
Last
()
(
map
[
string
]
interface
{},
error
)
{
if
obj
.
Remote
==
nil
{
return
nil
,
nil
// No object is live, return empty
}
accessor
,
err
:=
meta
.
Accessor
(
obj
.
Remote
)
if
err
!=
nil
{
return
nil
,
err
}
annots
:=
accessor
.
GetAnnotations
()
if
annots
==
nil
{
return
nil
,
nil
// Not an error, just empty.
}
return
obj
.
toMap
([]
byte
(
annots
[
corev1
.
LastAppliedConfigAnnotation
]))
_
,
result
,
err
:=
patcher
.
patch
(
obj
.
Info
.
Object
,
modified
,
obj
.
Info
.
Source
,
obj
.
Info
.
Namespace
,
obj
.
Info
.
Name
,
nil
)
return
result
,
err
}
func
(
obj
InfoObject
)
Name
()
string
{
...
...
@@ -342,59 +320,14 @@ 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
.
ToRESTMapper
()
if
err
!=
nil
{
return
nil
,
err
}
d
.
dclient
,
err
=
f
.
DynamicClient
()
if
err
!=
nil
{
return
nil
,
err
}
d
.
ns
,
_
,
_
=
f
.
ToRawKubeConfigLoader
()
.
Namespace
()
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.
func
RunDiff
(
f
cmdutil
.
Factory
,
diff
*
DiffProgram
,
options
*
DiffOptions
)
error
{
openapi
,
err
:=
f
.
OpenAPISchema
()
schema
,
err
:=
f
.
OpenAPISchema
()
if
err
!=
nil
{
return
err
}
parser
:=
&
parse
.
Factory
{
Resources
:
openapi
}
differ
,
err
:=
NewDiffer
(
"LIVE"
,
"MERGED"
)
if
err
!=
nil
{
...
...
@@ -413,29 +346,30 @@ func RunDiff(f cmdutil.Factory, diff *DiffProgram, options *DiffOptions) error {
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
}
remote
,
_
:=
dl
.
Download
(
info
)
local
:=
info
.
Object
.
DeepCopyObject
()
if
err
:=
info
.
Get
();
err
!=
nil
{
if
!
errors
.
IsNotFound
(
err
)
{
return
err
}
info
.
Object
=
nil
}
obj
:=
InfoObject
{
Remote
:
remote
,
Info
:
info
,
Parser
:
parser
,
Encoder
:
cmdutil
.
InternalVersionJSONEncoder
()
,
LocalObj
:
local
,
Info
:
info
,
Encoder
:
scheme
.
DefaultJSONEncoder
()
,
OpenAPI
:
schema
,
}
return
differ
.
Diff
(
obj
,
printer
)
...
...
@@ -444,7 +378,8 @@ func RunDiff(f cmdutil.Factory, diff *DiffProgram, options *DiffOptions) error {
return
err
}
differ
.
Run
(
diff
)
// Error ignore on purpose. diff(1) for example, returns an error if there is any diff.
_
=
differ
.
Run
(
diff
)
return
nil
}
pkg/kubectl/cmd/diff_test.go
View file @
e5964b3f
...
...
@@ -25,6 +25,8 @@ import (
"strings"
"testing"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/utils/exec"
)
...
...
@@ -41,12 +43,12 @@ func (f *FakeObject) Name() string {
return
f
.
name
}
func
(
f
*
FakeObject
)
Merged
()
(
map
[
string
]
interface
{}
,
error
)
{
return
f
.
merged
,
nil
func
(
f
*
FakeObject
)
Merged
()
(
runtime
.
Object
,
error
)
{
return
&
unstructured
.
Unstructured
{
Object
:
f
.
merged
}
,
nil
}
func
(
f
*
FakeObject
)
Live
()
(
map
[
string
]
interface
{},
error
)
{
return
f
.
live
,
nil
func
(
f
*
FakeObject
)
Live
()
runtime
.
Object
{
return
&
unstructured
.
Unstructured
{
Object
:
f
.
live
}
}
func
TestDiffProgram
(
t
*
testing
.
T
)
{
...
...
@@ -68,11 +70,11 @@ func TestDiffProgram(t *testing.T) {
func
TestPrinter
(
t
*
testing
.
T
)
{
printer
:=
Printer
{}
obj
:=
map
[
string
]
interface
{}{
obj
:=
&
unstructured
.
Unstructured
{
Object
:
map
[
string
]
interface
{}{
"string"
:
"string"
,
"list"
:
[]
int
{
1
,
2
,
3
},
"int"
:
12
,
}
}
}
buf
:=
bytes
.
Buffer
{}
printer
.
Print
(
obj
,
&
buf
)
want
:=
`int: 12
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment