Merge pull request #64449 from deads2k/cli-72-scrub

Automatic merge from submit-queue (batch tested with PRs 63328, 64316, 64444, 64449, 64453). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

cleanup some dead kubectl code and narrow scope of helpers

Found a lot of dead code in kubectl factory that we should scrub out


/assign @soltysh 
/assign @juanvallejo 


```release-note
NONE
```
This commit is contained in:
Kubernetes Submit Queue 2018-05-30 08:42:24 -07:00 committed by GitHub
commit 1123e5dd82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 18 additions and 447 deletions

View File

@ -148,7 +148,6 @@ pkg/kubectl/cmd/templates
pkg/kubectl/cmd/testing pkg/kubectl/cmd/testing
pkg/kubectl/cmd/util pkg/kubectl/cmd/util
pkg/kubectl/cmd/util/editor pkg/kubectl/cmd/util/editor
pkg/kubectl/cmd/util/jsonmerge
pkg/kubectl/cmd/util/sanity pkg/kubectl/cmd/util/sanity
pkg/kubectl/cmd/wait pkg/kubectl/cmd/wait
pkg/kubectl/genericclioptions pkg/kubectl/genericclioptions
@ -156,7 +155,6 @@ pkg/kubectl/genericclioptions/printers
pkg/kubectl/genericclioptions/resource pkg/kubectl/genericclioptions/resource
pkg/kubectl/metricsutil pkg/kubectl/metricsutil
pkg/kubectl/util pkg/kubectl/util
pkg/kubectl/util/crlf
pkg/kubectl/util/slice pkg/kubectl/util/slice
pkg/kubelet pkg/kubelet
pkg/kubelet/apis pkg/kubelet/apis

View File

@ -40,7 +40,6 @@ import (
"k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
scaleclient "k8s.io/client-go/scale"
oapi "k8s.io/kube-openapi/pkg/util/proto" oapi "k8s.io/kube-openapi/pkg/util/proto"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
@ -79,7 +78,6 @@ type ApplyOptions struct {
Validator validation.Schema Validator validation.Schema
Builder *resource.Builder Builder *resource.Builder
Mapper meta.RESTMapper Mapper meta.RESTMapper
Scaler scaleclient.ScalesGetter
DynamicClient dynamic.Interface DynamicClient dynamic.Interface
OpenAPISchema openapi.Resources OpenAPISchema openapi.Resources
@ -220,11 +218,6 @@ func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
return err return err
} }
o.Scaler, err = cmdutil.ScaleClientFn(f)
if err != nil {
return err
}
o.DynamicClient, err = f.DynamicClient() o.DynamicClient, err = f.DynamicClient()
if err != nil { if err != nil {
return err return err
@ -493,7 +486,6 @@ func (o *ApplyOptions) Run() error {
cascade: o.DeleteOptions.Cascade, cascade: o.DeleteOptions.Cascade,
dryRun: o.DryRun, dryRun: o.DryRun,
gracePeriod: o.DeleteOptions.GracePeriod, gracePeriod: o.DeleteOptions.GracePeriod,
scaler: o.Scaler,
toPrinter: o.ToPrinter, toPrinter: o.ToPrinter,
@ -583,8 +575,6 @@ type pruner struct {
dryRun bool dryRun bool
gracePeriod int gracePeriod int
scaler scaleclient.ScalesGetter
toPrinter func(string) (printers.ResourcePrinter, error) toPrinter func(string) (printers.ResourcePrinter, error)
out io.Writer out io.Writer

View File

@ -21,9 +21,9 @@ go_library(
deps = [ deps = [
"//pkg/apis/rbac:go_default_library", "//pkg/apis/rbac:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/set/env:go_default_library",
"//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/cmd/util/env:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/genericclioptions/printers:go_default_library", "//pkg/kubectl/genericclioptions/printers:go_default_library",
"//pkg/kubectl/genericclioptions/resource:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library",
@ -96,7 +96,10 @@ filegroup(
filegroup( filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [":package-srcs"], srcs = [
":package-srcs",
"//pkg/kubectl/cmd/set/env:all-srcs",
],
tags = ["automanaged"], tags = ["automanaged"],
visibility = [ visibility = [
"//build/visible_to:pkg_kubectl_cmd_set_CONSUMERS", "//build/visible_to:pkg_kubectl_cmd_set_CONSUMERS",

View File

@ -7,7 +7,7 @@ go_library(
"env_parse.go", "env_parse.go",
"env_resolve.go", "env_resolve.go",
], ],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/env", importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/set/env",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//pkg/api/v1/resource:go_default_library", "//pkg/api/v1/resource:go_default_library",

View File

@ -14,5 +14,5 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// Package env provides functions to incorporate environment variables into kubectl commands. // Package env provides functions to incorporate environment variables into set env.
package env // import "k8s.io/kubernetes/pkg/kubectl/cmd/util/env" package env

View File

@ -20,7 +20,6 @@ import (
"bufio" "bufio"
"fmt" "fmt"
"io" "io"
"os"
"regexp" "regexp"
"strings" "strings"
@ -28,24 +27,6 @@ import (
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
) )
// Env returns an environment variable if not nil, or a default value.
func Env(key string, defaultValue string) string {
val := os.Getenv(key)
if len(val) == 0 {
return defaultValue
}
return val
}
// GetEnv returns an environment value if not nil, and an ok boolean.
func GetEnv(key string) (string, bool) {
val := os.Getenv(key)
if len(val) == 0 {
return "", false
}
return val, true
}
var argumentEnvironment = regexp.MustCompile("(?ms)^(.+)\\=(.*)$") var argumentEnvironment = regexp.MustCompile("(?ms)^(.+)\\=(.*)$")
var validArgumentEnvironment = regexp.MustCompile("(?ms)^(\\w+)\\=(.*)$") var validArgumentEnvironment = regexp.MustCompile("(?ms)^(\\w+)\\=(.*)$")

View File

@ -19,36 +19,9 @@ package env
import ( import (
"fmt" "fmt"
"io" "io"
"os"
"strings" "strings"
) )
func ExampleEnv_defaultValue() {
fmt.Println(Env("TESTENVVAR", "default"))
// Output: default
}
func ExampleEnv_variableExists() {
os.Setenv("TESTENVVAR", "test value")
defer os.Unsetenv("TESTENVVAR")
fmt.Println(Env("TESTENVVAR", "default"))
// Output: test value
}
func ExampleGetEnv_variableExists() {
os.Setenv("THISVAREXISTS", "value")
defer os.Unsetenv("THISVAREXISTS")
fmt.Println(GetEnv("THISVAREXISTS"))
// Output:
// value true
}
func ExampleGetEnv_variableDoesNotExist() {
fmt.Println(GetEnv("THISVARDOESNOTEXIST"))
// Output:
// false
}
func ExampleIsEnvironmentArgument_true() { func ExampleIsEnvironmentArgument_true() {
test := "returns=true" test := "returns=true"
fmt.Println(IsEnvironmentArgument(test)) fmt.Println(IsEnvironmentArgument(test))

View File

@ -31,9 +31,9 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
envutil "k8s.io/kubernetes/pkg/kubectl/cmd/set/env"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
envutil "k8s.io/kubernetes/pkg/kubectl/cmd/util/env"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"

View File

@ -58,10 +58,7 @@ go_library(
go_test( go_test(
name = "go_default_test", name = "go_default_test",
srcs = [ srcs = ["helpers_test.go"],
"factory_test.go",
"helpers_test.go",
],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/api/testapi:go_default_library", "//pkg/api/testapi:go_default_library",
@ -90,8 +87,6 @@ filegroup(
srcs = [ srcs = [
":package-srcs", ":package-srcs",
"//pkg/kubectl/cmd/util/editor:all-srcs", "//pkg/kubectl/cmd/util/editor:all-srcs",
"//pkg/kubectl/cmd/util/env:all-srcs",
"//pkg/kubectl/cmd/util/jsonmerge:all-srcs",
"//pkg/kubectl/cmd/util/openapi:all-srcs", "//pkg/kubectl/cmd/util/openapi:all-srcs",
"//pkg/kubectl/cmd/util/sanity:all-srcs", "//pkg/kubectl/cmd/util/sanity:all-srcs",
], ],

View File

@ -18,11 +18,11 @@ go_library(
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/cmd/util/editor/crlf:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/genericclioptions/printers:go_default_library", "//pkg/kubectl/genericclioptions/printers:go_default_library",
"//pkg/kubectl/genericclioptions/resource:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library",
"//pkg/kubectl/scheme:go_default_library", "//pkg/kubectl/scheme:go_default_library",
"//pkg/kubectl/util/crlf:go_default_library",
"//pkg/kubectl/util/term:go_default_library", "//pkg/kubectl/util/term:go_default_library",
"//vendor/github.com/evanphx/json-patch:go_default_library", "//vendor/github.com/evanphx/json-patch:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
@ -57,7 +57,10 @@ filegroup(
filegroup( filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [":package-srcs"], srcs = [
":package-srcs",
"//pkg/kubectl/cmd/util/editor/crlf:all-srcs",
],
tags = ["automanaged"], tags = ["automanaged"],
visibility = [ visibility = [
"//build/visible_to:pkg_kubectl_cmd_util_editor_CONSUMERS", "//build/visible_to:pkg_kubectl_cmd_util_editor_CONSUMERS",

View File

@ -8,7 +8,7 @@ load(
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = ["crlf.go"], srcs = ["crlf.go"],
importpath = "k8s.io/kubernetes/pkg/kubectl/util/crlf", importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/crlf",
) )
filegroup( filegroup(

View File

@ -51,7 +51,7 @@ func (w crlfWriter) Write(b []byte) (n int, err error) {
} }
return written + n, err return written + n, err
} }
written += 1 written++
i = next + 1 i = next + 1
} }
} }

View File

@ -45,11 +45,11 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/crlf"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/crlf"
) )
// EditOptions contains all the options for running edit cli command. // EditOptions contains all the options for running edit cli command.

View File

@ -17,15 +17,10 @@ limitations under the License.
package util package util
import ( import (
"fmt"
"strconv"
"strings"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
@ -73,26 +68,3 @@ type Factory interface {
// OpenAPISchema returns the schema openapi schema definition // OpenAPISchema returns the schema openapi schema definition
OpenAPISchema() (openapi.Resources, error) OpenAPISchema() (openapi.Resources, error)
} }
func makePortsString(ports []api.ServicePort, useNodePort bool) string {
pieces := make([]string, len(ports))
for ix := range ports {
var port int32
if useNodePort {
port = ports[ix].NodePort
} else {
port = ports[ix].Port
}
pieces[ix] = fmt.Sprintf("%s:%d", strings.ToLower(string(ports[ix].Protocol)), port)
}
return strings.Join(pieces, ",")
}
// Extracts the protocols exposed by a service from the given service spec.
func getServiceProtocols(spec api.ServiceSpec) map[string]string {
result := make(map[string]string)
for _, servicePort := range spec.Ports {
result[strconv.Itoa(int(servicePort.Port))] = string(servicePort.Protocol)
}
return result
}

View File

@ -28,7 +28,6 @@ import (
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
scaleclient "k8s.io/client-go/scale"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
"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"
@ -189,24 +188,6 @@ func (f *factoryImpl) OpenAPISchema() (openapi.Resources, error) {
return f.openAPIGetter.getter.Get() return f.openAPIGetter.getter.Get()
} }
func (f *factoryImpl) ScaleClient() (scaleclient.ScalesGetter, error) {
discoClient, err := f.clientGetter.ToDiscoveryClient()
if err != nil {
return nil, err
}
restClient, err := f.RESTClient()
if err != nil {
return nil, err
}
resolver := scaleclient.NewDiscoveryScaleKindResolver(discoClient)
mapper, err := f.clientGetter.ToRESTMapper()
if err != nil {
return nil, err
}
return scaleclient.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver), nil
}
// this method exists to help us find the points still relying on internal types. // this method exists to help us find the points still relying on internal types.
func InternalVersionDecoder() runtime.Decoder { func InternalVersionDecoder() runtime.Decoder {
return legacyscheme.Codecs.UniversalDecoder() return legacyscheme.Codecs.UniversalDecoder()

View File

@ -1,79 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"testing"
api "k8s.io/kubernetes/pkg/apis/core"
)
func TestMakePortsString(t *testing.T) {
tests := []struct {
ports []api.ServicePort
useNodePort bool
expectedOutput string
}{
{ports: nil, expectedOutput: ""},
{ports: []api.ServicePort{}, expectedOutput: ""},
{ports: []api.ServicePort{
{
Port: 80,
Protocol: "TCP",
},
},
expectedOutput: "tcp:80",
},
{ports: []api.ServicePort{
{
Port: 80,
Protocol: "TCP",
},
{
Port: 8080,
Protocol: "UDP",
},
{
Port: 9000,
Protocol: "TCP",
},
},
expectedOutput: "tcp:80,udp:8080,tcp:9000",
},
{ports: []api.ServicePort{
{
Port: 80,
NodePort: 9090,
Protocol: "TCP",
},
{
Port: 8080,
NodePort: 80,
Protocol: "UDP",
},
},
useNodePort: true,
expectedOutput: "tcp:9090,udp:80",
},
}
for _, test := range tests {
output := makePortsString(test.ports, test.useNodePort)
if output != test.expectedOutput {
t.Errorf("expected: %s, saw: %s.", test.expectedOutput, output)
}
}
}

View File

@ -62,7 +62,6 @@ const (
ConfigMapV1GeneratorName = "configmap/v1" ConfigMapV1GeneratorName = "configmap/v1"
ClusterRoleBindingV1GeneratorName = "clusterrolebinding.rbac.authorization.k8s.io/v1alpha1" ClusterRoleBindingV1GeneratorName = "clusterrolebinding.rbac.authorization.k8s.io/v1alpha1"
RoleBindingV1GeneratorName = "rolebinding.rbac.authorization.k8s.io/v1alpha1" RoleBindingV1GeneratorName = "rolebinding.rbac.authorization.k8s.io/v1alpha1"
ClusterV1Beta1GeneratorName = "cluster/v1beta1"
PodDisruptionBudgetV1GeneratorName = "poddisruptionbudget/v1beta1" PodDisruptionBudgetV1GeneratorName = "poddisruptionbudget/v1beta1"
PodDisruptionBudgetV2GeneratorName = "poddisruptionbudget/v1beta1/v2" PodDisruptionBudgetV2GeneratorName = "poddisruptionbudget/v1beta1/v2"
PriorityClassV1Alpha1GeneratorName = "priorityclass/v1alpha1" PriorityClassV1Alpha1GeneratorName = "priorityclass/v1alpha1"

View File

@ -300,16 +300,6 @@ func IsFilenameSliceEmpty(filenames []string) bool {
return len(filenames) == 0 return len(filenames) == 0
} }
// Whether this cmd need watching objects.
func isWatch(cmd *cobra.Command) bool {
if w, err := cmd.Flags().GetBool("watch"); err == nil && w {
return true
}
wo, err := cmd.Flags().GetBool("watch-only")
return err == nil && wo
}
func GetFlagString(cmd *cobra.Command, flag string) string { func GetFlagString(cmd *cobra.Command, flag string) string {
s, err := cmd.Flags().GetString(flag) s, err := cmd.Flags().GetString(flag)
if err != nil { if err != nil {
@ -336,15 +326,6 @@ func GetFlagStringArray(cmd *cobra.Command, flag string) []string {
return s return s
} }
// GetWideFlag is used to determine if "-o wide" is used
func GetWideFlag(cmd *cobra.Command) bool {
f := cmd.Flags().Lookup("output")
if f != nil && f.Value != nil && f.Value.String() == "wide" {
return true
}
return false
}
func GetFlagBool(cmd *cobra.Command, flag string) bool { func GetFlagBool(cmd *cobra.Command, flag string) bool {
b, err := cmd.Flags().GetBool(flag) b, err := cmd.Flags().GetBool(flag)
if err != nil { if err != nil {

View File

@ -1,32 +0,0 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["jsonmerge.go"],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/evanphx/json-patch:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/mergepatch:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = [
"//build/visible_to:pkg_kubectl_cmd_util_jsonmerge_CONSUMERS",
],
)

View File

@ -1,193 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package jsonmerge
import (
"encoding/json"
"fmt"
"github.com/evanphx/json-patch"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/util/mergepatch"
"k8s.io/apimachinery/pkg/util/yaml"
)
// Delta represents a change between two JSON documents.
type Delta struct {
original []byte
edit []byte
preconditions []PreconditionFunc
}
// PreconditionFunc is a test to verify that an incompatible change
// has occurred before an Apply can be successful.
type PreconditionFunc func(interface{}) (hold bool, message string)
// AddPreconditions adds precondition checks to a change which must
// be satisfied before an Apply is considered successful. If a
// precondition returns false, the Apply is failed with
// ErrPreconditionFailed.
func (d *Delta) AddPreconditions(fns ...PreconditionFunc) {
d.preconditions = append(d.preconditions, fns...)
}
// RequireKeyUnchanged creates a precondition function that fails
// if the provided key is present in the diff (indicating its value
// has changed).
func RequireKeyUnchanged(key string) PreconditionFunc {
return func(diff interface{}) (bool, string) {
m, ok := diff.(map[string]interface{})
if !ok {
return true, ""
}
// the presence of key in a diff means that its value has been changed, therefore
// we should fail the precondition.
_, ok = m[key]
if ok {
return false, key + " should not be changed\n"
} else {
return true, ""
}
}
}
// RequireMetadataKeyUnchanged creates a precondition function that fails
// if the metadata.key is present in the diff (indicating its value
// has changed).
func RequireMetadataKeyUnchanged(key string) PreconditionFunc {
return func(diff interface{}) (bool, string) {
m, ok := diff.(map[string]interface{})
if !ok {
return true, ""
}
m1, ok := m["metadata"]
if !ok {
return true, ""
}
m2, ok := m1.(map[string]interface{})
if !ok {
return true, ""
}
_, ok = m2[key]
if ok {
return false, "metadata." + key + " should not be changed\n"
} else {
return true, ""
}
}
}
// TestPreconditions test if preconditions hold given the edit
func TestPreconditionsHold(edit []byte, preconditions []PreconditionFunc) (bool, string) {
diff := make(map[string]interface{})
if err := json.Unmarshal(edit, &diff); err != nil {
return false, err.Error()
}
for _, fn := range preconditions {
if hold, msg := fn(diff); !hold {
return false, msg
}
}
return true, ""
}
// NewDelta accepts two JSON or YAML documents and calculates the difference
// between them. It returns a Delta object which can be used to resolve
// conflicts against a third version with a common parent, or an error
// if either document is in error.
func NewDelta(from, to []byte) (*Delta, error) {
d := &Delta{}
before, err := yaml.ToJSON(from)
if err != nil {
return nil, err
}
after, err := yaml.ToJSON(to)
if err != nil {
return nil, err
}
diff, err := jsonpatch.CreateMergePatch(before, after)
if err != nil {
return nil, err
}
glog.V(6).Infof("Patch created from:\n%s\n%s\n%s", string(before), string(after), string(diff))
d.original = before
d.edit = diff
return d, nil
}
// Apply attempts to apply the changes described by Delta onto latest,
// returning an error if the changes cannot be applied cleanly.
// IsConflicting will be true if the changes overlap, otherwise a
// generic error will be returned.
func (d *Delta) Apply(latest []byte) ([]byte, error) {
base, err := yaml.ToJSON(latest)
if err != nil {
return nil, err
}
changes, err := jsonpatch.CreateMergePatch(d.original, base)
if err != nil {
return nil, err
}
diff1 := make(map[string]interface{})
if err := json.Unmarshal(d.edit, &diff1); err != nil {
return nil, err
}
diff2 := make(map[string]interface{})
if err := json.Unmarshal(changes, &diff2); err != nil {
return nil, err
}
for _, fn := range d.preconditions {
hold1, _ := fn(diff1)
hold2, _ := fn(diff2)
if !hold1 || !hold2 {
return nil, ErrPreconditionFailed
}
}
glog.V(6).Infof("Testing for conflict between:\n%s\n%s", string(d.edit), string(changes))
hasConflicts, err := mergepatch.HasConflicts(diff1, diff2)
if err != nil {
return nil, err
}
if hasConflicts {
return nil, ErrConflict
}
return jsonpatch.MergePatch(base, d.edit)
}
// IsConflicting returns true if the provided error indicates a
// conflict exists between the original changes and the applied
// changes.
func IsConflicting(err error) bool {
return err == ErrConflict
}
// IsPreconditionFailed returns true if the provided error indicates
// a Delta precondition did not succeed.
func IsPreconditionFailed(err error) bool {
return err == ErrPreconditionFailed
}
var ErrPreconditionFailed = fmt.Errorf("a precondition failed")
var ErrConflict = fmt.Errorf("changes are in conflict")
func (d *Delta) Edit() []byte {
return d.edit
}

View File

@ -98,7 +98,6 @@ filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [ srcs = [
":package-srcs", ":package-srcs",
"//pkg/kubectl/util/crlf:all-srcs",
"//pkg/kubectl/util/hash:all-srcs", "//pkg/kubectl/util/hash:all-srcs",
"//pkg/kubectl/util/i18n:all-srcs", "//pkg/kubectl/util/i18n:all-srcs",
"//pkg/kubectl/util/logs:all-srcs", "//pkg/kubectl/util/logs:all-srcs",