Commit b52aff72 authored by Thomas Runyon's avatar Thomas Runyon

Merge remote-tracking branch 'upstream/master' into test-cmd-what

parents 8772e263 0c2613c7
......@@ -84986,7 +84986,7 @@
"type": "string"
},
"enableServiceLinks": {
"description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links.",
"description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.",
"type": "boolean"
},
"hostAliases": {
......@@ -89234,9 +89234,6 @@
"io.k8s.api.rbac.v1.ClusterRole": {
"description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.",
"type": "object",
"required": [
"rules"
],
"properties": {
"aggregationRule": {
"description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.",
......@@ -89426,9 +89423,6 @@
"io.k8s.api.rbac.v1.Role": {
"description": "Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.",
"type": "object",
"required": [
"rules"
],
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
......@@ -89632,9 +89626,6 @@
"io.k8s.api.rbac.v1alpha1.ClusterRole": {
"description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.",
"type": "object",
"required": [
"rules"
],
"properties": {
"aggregationRule": {
"description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.",
......@@ -89824,9 +89815,6 @@
"io.k8s.api.rbac.v1alpha1.Role": {
"description": "Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.",
"type": "object",
"required": [
"rules"
],
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
......@@ -90030,9 +90018,6 @@
"io.k8s.api.rbac.v1beta1.ClusterRole": {
"description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.",
"type": "object",
"required": [
"rules"
],
"properties": {
"aggregationRule": {
"description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.",
......@@ -90222,9 +90207,6 @@
"io.k8s.api.rbac.v1beta1.Role": {
"description": "Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.",
"type": "object",
"required": [
"rules"
],
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
......@@ -66,12 +66,15 @@ func createAggregatorConfig(
// override genericConfig.AdmissionControl with kube-aggregator's scheme,
// because aggregator apiserver should use its own scheme to convert its own resources.
commandOptions.Admission.ApplyTo(
err := commandOptions.Admission.ApplyTo(
&genericConfig,
externalInformers,
genericConfig.LoopbackClientConfig,
aggregatorscheme.Scheme,
pluginInitializers...)
if err != nil {
return nil, err
}
// copy the etcd options so we don't mutate originals.
etcdOptions := *commandOptions.Etcd
......@@ -87,7 +90,6 @@ func createAggregatorConfig(
return nil, err
}
var err error
var certBytes, keyBytes []byte
if len(commandOptions.ProxyClientCertFile) > 0 && len(commandOptions.ProxyClientKeyFile) > 0 {
certBytes, err = ioutil.ReadFile(commandOptions.ProxyClientCertFile)
......@@ -133,7 +135,7 @@ func createAggregatorServer(aggregatorConfig *aggregatorapiserver.Config, delega
apiExtensionInformers.Apiextensions().InternalVersion().CustomResourceDefinitions(),
autoRegistrationController)
aggregatorServer.GenericAPIServer.AddPostStartHook("kube-apiserver-autoregistration", func(context genericapiserver.PostStartHookContext) error {
err = aggregatorServer.GenericAPIServer.AddPostStartHook("kube-apiserver-autoregistration", func(context genericapiserver.PostStartHookContext) error {
go crdRegistrationController.Run(5, context.StopCh)
go func() {
// let the CRD controller process the initial set of CRDs before starting the autoregistration controller.
......@@ -146,14 +148,20 @@ func createAggregatorServer(aggregatorConfig *aggregatorapiserver.Config, delega
}()
return nil
})
if err != nil {
return nil, err
}
aggregatorServer.GenericAPIServer.AddHealthzChecks(
err = aggregatorServer.GenericAPIServer.AddHealthzChecks(
makeAPIServiceAvailableHealthzCheck(
"autoregister-completion",
apiServices,
aggregatorServer.APIRegistrationInformers.Apiregistration().InternalVersion().APIServices(),
),
)
if err != nil {
return nil, err
}
return aggregatorServer, nil
}
......
......@@ -48,12 +48,15 @@ func createAPIExtensionsConfig(
// override genericConfig.AdmissionControl with apiextensions' scheme,
// because apiextentions apiserver should use its own scheme to convert resources.
commandOptions.Admission.ApplyTo(
err := commandOptions.Admission.ApplyTo(
&genericConfig,
externalInformers,
genericConfig.LoopbackClientConfig,
apiextensionsapiserver.Scheme,
pluginInitializers...)
if err != nil {
return nil, err
}
// copy the etcd options so we don't mutate originals.
etcdOptions := *commandOptions.Etcd
......
......@@ -32,6 +32,7 @@ go_library(
"//pkg/proxy/ipvs:go_default_library",
"//pkg/proxy/userspace:go_default_library",
"//pkg/util/configz:go_default_library",
"//pkg/util/filesystem:go_default_library",
"//pkg/util/flag:go_default_library",
"//pkg/util/ipset:go_default_library",
"//pkg/util/iptables:go_default_library",
......@@ -63,6 +64,7 @@ go_library(
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
"//staging/src/k8s.io/component-base/config:go_default_library",
"//staging/src/k8s.io/kube-proxy/config/v1alpha1:go_default_library",
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
......
......@@ -63,6 +63,7 @@ import (
"k8s.io/kubernetes/pkg/proxy/ipvs"
"k8s.io/kubernetes/pkg/proxy/userspace"
"k8s.io/kubernetes/pkg/util/configz"
"k8s.io/kubernetes/pkg/util/filesystem"
utilflag "k8s.io/kubernetes/pkg/util/flag"
utilipset "k8s.io/kubernetes/pkg/util/ipset"
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
......@@ -74,6 +75,7 @@ import (
"k8s.io/utils/exec"
utilpointer "k8s.io/utils/pointer"
"github.com/fsnotify/fsnotify"
"github.com/prometheus/client_golang/prometheus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
......@@ -87,6 +89,11 @@ const (
proxyModeKernelspace = "kernelspace"
)
// proxyRun defines the interface to run a specified ProxyServer
type proxyRun interface {
Run() error
}
// Options contains everything necessary to create and run a proxy server.
type Options struct {
// ConfigFile is the location of the proxy server's configuration file.
......@@ -102,6 +109,12 @@ type Options struct {
WindowsService bool
// config is the proxy server's configuration object.
config *kubeproxyconfig.KubeProxyConfiguration
// watcher is used to watch on the update change of ConfigFile
watcher filesystem.FSWatcher
// proxyServer is the interface to run the proxy server
proxyServer proxyRun
// errCh is the channel that errors will be sent
errCh chan error
// The fields below here are placeholders for flags that can't be directly mapped into
// config.KubeProxyConfiguration.
......@@ -191,6 +204,7 @@ func NewOptions() *Options {
scheme: scheme.Scheme,
codecs: scheme.Codecs,
CleanupIPVS: true,
errCh: make(chan error),
}
}
......@@ -209,6 +223,10 @@ func (o *Options) Complete() error {
} else {
o.config = c
}
if err := o.initWatcher(); err != nil {
return err
}
}
if err := o.processHostnameOverrideFlag(); err != nil {
......@@ -218,10 +236,39 @@ func (o *Options) Complete() error {
if err := utilfeature.DefaultMutableFeatureGate.SetFromMap(o.config.FeatureGates); err != nil {
return err
}
return nil
}
// Creates a new filesystem watcher and adds watches for the config file.
func (o *Options) initWatcher() error {
fswatcher := filesystem.NewFsnotifyWatcher()
err := fswatcher.Init(o.eventHandler, o.errorHandler)
if err != nil {
return err
}
err = fswatcher.AddWatch(o.ConfigFile)
if err != nil {
return err
}
o.watcher = fswatcher
return nil
}
func (o *Options) eventHandler(ent fsnotify.Event) {
eventOpIs := func(Op fsnotify.Op) bool {
return ent.Op&Op == Op
}
if eventOpIs(fsnotify.Write) || eventOpIs(fsnotify.Rename) {
// error out when ConfigFile is updated
o.errCh <- fmt.Errorf("content of the proxy server's configuration file was updated")
}
o.errCh <- nil
}
func (o *Options) errorHandler(err error) {
o.errCh <- err
}
// processHostnameOverrideFlag processes hostname-override flag
func (o *Options) processHostnameOverrideFlag() error {
// Check if hostname-override flag is set and use value since configFile always overrides
......@@ -241,7 +288,6 @@ func (o *Options) Validate(args []string) error {
if len(args) != 0 {
return errors.New("no arguments are supported")
}
if errs := validation.Validate(o.config); len(errs) != 0 {
return errs.ToAggregate()
}
......@@ -250,6 +296,7 @@ func (o *Options) Validate(args []string) error {
}
func (o *Options) Run() error {
defer close(o.errCh)
if len(o.WriteConfigTo) > 0 {
return o.writeConfigFile()
}
......@@ -258,8 +305,31 @@ func (o *Options) Run() error {
if err != nil {
return err
}
o.proxyServer = proxyServer
return o.runLoop()
}
// runLoop will watch on the update change of the proxy server's configuration file.
// Return an error when updated
func (o *Options) runLoop() error {
if o.watcher != nil {
o.watcher.Run()
}
// run the proxy in goroutine
go func() {
err := o.proxyServer.Run()
o.errCh <- err
}()
return proxyServer.Run()
for {
select {
case err := <-o.errCh:
if err != nil {
return err
}
}
}
}
func (o *Options) writeConfigFile() error {
......
......@@ -18,6 +18,8 @@ package app
import (
"fmt"
"io/ioutil"
"os"
"reflect"
"runtime"
"strings"
......@@ -410,3 +412,135 @@ func TestProcessHostnameOverrideFlag(t *testing.T) {
})
}
}
func TestConfigChange(t *testing.T) {
setUp := func() (*os.File, string, error) {
tempDir := os.TempDir()
file, err := ioutil.TempFile(tempDir, "kube-proxy-config-")
if err != nil {
return nil, "", fmt.Errorf("unexpected error when creating temp file: %v", err)
}
_, err = file.WriteString(`apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
clientConnection:
acceptContentTypes: ""
burst: 10
contentType: application/vnd.kubernetes.protobuf
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
qps: 5
clusterCIDR: 10.244.0.0/16
configSyncPeriod: 15m0s
conntrack:
max: null
maxPerCore: 32768
min: 131072
tcpCloseWaitTimeout: 1h0m0s
tcpEstablishedTimeout: 24h0m0s
enableProfiling: false
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: ""
iptables:
masqueradeAll: false
masqueradeBit: 14
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: ""
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
resourceContainer: /kube-proxy
udpIdleTimeout: 250ms`)
if err != nil {
return nil, "", fmt.Errorf("unexpected error when writing content to temp kube-proxy config file: %v", err)
}
return file, tempDir, nil
}
tearDown := func(file *os.File, tempDir string) {
file.Close()
os.RemoveAll(tempDir)
}
testCases := []struct {
name string
proxyServer proxyRun
append bool
expectedErr string
}{
{
name: "update config file",
proxyServer: new(fakeProxyServerLongRun),
append: true,
expectedErr: "content of the proxy server's configuration file was updated",
},
{
name: "fake error",
proxyServer: new(fakeProxyServerError),
expectedErr: "mocking error from ProxyServer.Run()",
},
}
for _, tc := range testCases {
file, tempDir, err := setUp()
if err != nil {
t.Fatalf("unexpected error when setting up environment: %v", err)
}
opt := NewOptions()
opt.ConfigFile = file.Name()
err = opt.Complete()
if err != nil {
t.Fatal(err)
}
opt.proxyServer = tc.proxyServer
errCh := make(chan error)
go func() {
errCh <- opt.runLoop()
}()
if tc.append {
file.WriteString("append fake content")
}
select {
case err := <-errCh:
if err != nil {
if !strings.Contains(err.Error(), tc.expectedErr) {
t.Errorf("[%s] Expected error containing %v, got %v", tc.name, tc.expectedErr, err)
}
}
case <-time.After(10 * time.Second):
t.Errorf("[%s] Timeout: unable to get any events or internal timeout.", tc.name)
}
tearDown(file, tempDir)
}
}
type fakeProxyServerLongRun struct{}
// Run runs the specified ProxyServer.
func (s *fakeProxyServerLongRun) Run() error {
for {
time.Sleep(2 * time.Second)
}
}
type fakeProxyServerError struct{}
// Run runs the specified ProxyServer.
func (s *fakeProxyServerError) Run() error {
for {
time.Sleep(2 * time.Second)
return fmt.Errorf("mocking error from ProxyServer.Run()")
}
}
......@@ -62,10 +62,8 @@ func NewCmdReset(in io.Reader, out io.Writer) *cobra.Command {
kubeadmutil.CheckErr(err)
kubeConfigFile = cmdutil.FindExistingKubeConfig(kubeConfigFile)
if _, err := os.Stat(kubeConfigFile); !os.IsNotExist(err) {
client, err = getClientset(kubeConfigFile, false)
kubeadmutil.CheckErr(err)
}
client, err = getClientset(kubeConfigFile, false)
kubeadmutil.CheckErr(err)
if criSocketPath == "" {
criSocketPath, err = resetDetectCRISocket(client)
......
......@@ -27,6 +27,7 @@ import (
"k8s.io/klog"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
netutil "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/version"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
......@@ -48,15 +49,9 @@ func MarshalKubeadmConfigObject(obj runtime.Object) ([]byte, error) {
}
}
// DetectUnsupportedVersion reads YAML bytes, extracts the TypeMeta information and errors out with an user-friendly message if the API spec is too old for this kubeadm version
func DetectUnsupportedVersion(b []byte) error {
gvks, err := kubeadmutil.GroupVersionKindsFromBytes(b)
if err != nil {
return err
}
// TODO: On our way to making the kubeadm API beta and higher, give good user output in case they use an old config file with a new kubeadm version, and
// tell them how to upgrade. The support matrix will look something like this now and in the future:
// ValidateSupportedVersion checks if a supplied GroupVersion is not on the list of unsupported GVs. If it is, an error is returned.
func ValidateSupportedVersion(gv schema.GroupVersion) error {
// The support matrix will look something like this now and in the future:
// v1.10 and earlier: v1alpha1
// v1.11: v1alpha1 read-only, writes only v1alpha2 config
// v1.12: v1alpha2 read-only, writes only v1alpha3 config. Warns if the user tries to use v1alpha1
......@@ -65,27 +60,9 @@ func DetectUnsupportedVersion(b []byte) error {
"kubeadm.k8s.io/v1alpha1": "v1.11",
"kubeadm.k8s.io/v1alpha2": "v1.12",
}
// If we find an old API version in this gvk list, error out and tell the user why this doesn't work
knownKinds := map[string]bool{}
for _, gvk := range gvks {
if useKubeadmVersion := oldKnownAPIVersions[gvk.GroupVersion().String()]; len(useKubeadmVersion) != 0 {
return errors.Errorf("your configuration file uses an old API spec: %q. Please use kubeadm %s instead and run 'kubeadm config migrate --old-config old.yaml --new-config new.yaml', which will write the new, similar spec using a newer API version.", gvk.GroupVersion().String(), useKubeadmVersion)
}
knownKinds[gvk.Kind] = true
}
// InitConfiguration and JoinConfiguration may not apply together, warn if more than one is specified
mutuallyExclusive := []string{constants.InitConfigurationKind, constants.JoinConfigurationKind}
mutuallyExclusiveCount := 0
for _, kind := range mutuallyExclusive {
if knownKinds[kind] {
mutuallyExclusiveCount++
}
if useKubeadmVersion := oldKnownAPIVersions[gv.String()]; useKubeadmVersion != "" {
return errors.Errorf("your configuration file uses an old API spec: %q. Please use kubeadm %s instead and run 'kubeadm config migrate --old-config old.yaml --new-config new.yaml', which will write the new, similar spec using a newer API version.", gv.String(), useKubeadmVersion)
}
if mutuallyExclusiveCount > 1 {
klog.Warningf("WARNING: Detected resource kinds that may not apply: %v", mutuallyExclusive)
}
return nil
}
......
......@@ -17,187 +17,66 @@ limitations under the License.
package config
import (
"bytes"
"io/ioutil"
"os"
"testing"
"github.com/lithammer/dedent"
"k8s.io/apimachinery/pkg/runtime/schema"
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)
var files = map[string][]byte{
"Master_v1alpha1": []byte(`
apiVersion: kubeadm.k8s.io/v1alpha1
kind: InitConfiguration
`),
"Node_v1alpha1": []byte(`
apiVersion: kubeadm.k8s.io/v1alpha1
kind: NodeConfiguration
`),
"Master_v1alpha2": []byte(`
apiVersion: kubeadm.k8s.io/v1alpha2
kind: MasterConfiguration
`),
"Node_v1alpha2": []byte(`
apiVersion: kubeadm.k8s.io/v1alpha2
kind: NodeConfiguration
`),
"Init_v1alpha3": []byte(`
apiVersion: kubeadm.k8s.io/v1alpha3
kind: InitConfiguration
`),
"Join_v1alpha3": []byte(`
apiVersion: kubeadm.k8s.io/v1alpha3
kind: JoinConfiguration
`),
"Init_v1beta1": []byte(`
apiVersion: kubeadm.k8s.io/v1beta1
kind: InitConfiguration
`),
"Join_v1beta1": []byte(`
apiVersion: kubeadm.k8s.io/v1beta1
kind: JoinConfiguration
`),
"NoKind": []byte(`
apiVersion: baz.k8s.io/v1
foo: foo
bar: bar
`),
"NoAPIVersion": []byte(`
kind: Bar
foo: foo
bar: bar
`),
"Foo": []byte(`
apiVersion: foo.k8s.io/v1
kind: Foo
`),
}
func TestValidateSupportedVersion(t *testing.T) {
const KubeadmGroupName = "kubeadm.k8s.io"
func TestDetectUnsupportedVersion(t *testing.T) {
var tests = []struct {
name string
fileContents []byte
expectedErr bool
tests := []struct {
gv schema.GroupVersion
expectedErr bool
}{
{
name: "Master_v1alpha1",
fileContents: files["Master_v1alpha1"],
expectedErr: true,
},
{
name: "Node_v1alpha1",
fileContents: files["Node_v1alpha1"],
expectedErr: true,
},
{
name: "Master_v1alpha2",
fileContents: files["Master_v1alpha2"],
expectedErr: true,
},
{
name: "Node_v1alpha2",
fileContents: files["Node_v1alpha2"],
expectedErr: true,
},
{
name: "Init_v1alpha3",
fileContents: files["Init_v1alpha3"],
},
{
name: "Join_v1alpha3",
fileContents: files["Join_v1alpha3"],
},
{
name: "Init_v1beta1",
fileContents: files["Init_v1beta1"],
},
{
name: "Join_v1beta1",
fileContents: files["Join_v1beta1"],
},
{
name: "DuplicateInit v1alpha3",
fileContents: bytes.Join([][]byte{files["Init_v1alpha3"], files["Init_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
expectedErr: true,
},
{
name: "DuplicateInit v1beta1",
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Init_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
expectedErr: true,
},
{
name: "DuplicateInit v1beta1 and v1alpha3",
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Init_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
expectedErr: true,
},
{
name: "DuplicateJoin v1alpha3",
fileContents: bytes.Join([][]byte{files["Join_v1alpha3"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
expectedErr: true,
},
{
name: "DuplicateJoin v1beta1",
fileContents: bytes.Join([][]byte{files["Join_v1beta1"], files["Join_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
expectedErr: true,
},
{
name: "DuplicateJoin v1beta1 and v1alpha3",
fileContents: bytes.Join([][]byte{files["Join_v1beta1"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
expectedErr: true,
},
{
name: "NoKind",
fileContents: files["NoKind"],
expectedErr: true,
},
{
name: "NoAPIVersion",
fileContents: files["NoAPIVersion"],
expectedErr: true,
},
{
name: "Ignore other Kind",
fileContents: bytes.Join([][]byte{files["Foo"], files["Master_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
},
{
name: "Ignore other Kind",
fileContents: bytes.Join([][]byte{files["Foo"], files["Master_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
gv: schema.GroupVersion{
Group: KubeadmGroupName,
Version: "v1alpha1",
},
expectedErr: true,
},
// CanMixInitJoin cases used to be MustNotMixInitJoin, however due to UX issues DetectUnsupportedVersion had to tolerate that.
// So the following tests actually verify, that Init and Join can be mixed together with no error.
{
name: "CanMixInitJoin v1alpha3",
fileContents: bytes.Join([][]byte{files["Init_v1alpha3"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
expectedErr: false,
gv: schema.GroupVersion{
Group: KubeadmGroupName,
Version: "v1alpha2",
},
expectedErr: true,
},
{
name: "CanMixInitJoin v1alpha3 - v1beta1",
fileContents: bytes.Join([][]byte{files["Init_v1alpha3"], files["Join_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
expectedErr: false,
gv: schema.GroupVersion{
Group: KubeadmGroupName,
Version: "v1alpha3",
},
},
{
name: "CanMixInitJoin v1beta1 - v1alpha3",
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
expectedErr: false,
gv: schema.GroupVersion{
Group: KubeadmGroupName,
Version: "v1beta1",
},
},
{
name: "CanMixInitJoin v1beta1",
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Join_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
expectedErr: false,
gv: schema.GroupVersion{
Group: "foo.k8s.io",
Version: "v1",
},
},
}
for _, rt := range tests {
t.Run(rt.name, func(t2 *testing.T) {
err := DetectUnsupportedVersion(rt.fileContents)
if (err != nil) != rt.expectedErr {
t2.Errorf("expected error: %t, actual: %t", rt.expectedErr, err != nil)
t.Run(rt.gv.String(), func(t *testing.T) {
err := ValidateSupportedVersion(rt.gv)
if rt.expectedErr && err == nil {
t.Error("unexpected success")
} else if !rt.expectedErr && err != nil {
t.Errorf("unexpected failure: %v", err)
}
})
}
......
......@@ -208,16 +208,17 @@ func BytesToInternalConfig(b []byte) (*kubeadmapi.InitConfiguration, error) {
var clustercfg *kubeadmapi.ClusterConfiguration
decodedComponentConfigObjects := map[componentconfigs.RegistrationKind]runtime.Object{}
if err := DetectUnsupportedVersion(b); err != nil {
return nil, err
}
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
if err != nil {
return nil, err
}
for gvk, fileContent := range gvkmap {
// first, check if this GVK is supported one
if err := ValidateSupportedVersion(gvk.GroupVersion()); err != nil {
return nil, err
}
// verify the validity of the YAML
strict.VerifyUnmarshalStrict(fileContent, gvk)
......
......@@ -72,10 +72,6 @@ func JoinConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedc
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
}
if err := DetectUnsupportedVersion(b); err != nil {
return nil, err
}
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
if err != nil {
return nil, err
......@@ -83,11 +79,20 @@ func JoinConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedc
joinBytes := []byte{}
for gvk, bytes := range gvkmap {
if gvk.Kind == constants.JoinConfigurationKind {
joinBytes = bytes
// verify the validity of the YAML
strict.VerifyUnmarshalStrict(bytes, gvk)
// not interested in anything other than JoinConfiguration
if gvk.Kind != constants.JoinConfigurationKind {
continue
}
// check if this version is supported one
if err := ValidateSupportedVersion(gvk.GroupVersion()); err != nil {
return nil, err
}
// verify the validity of the YAML
strict.VerifyUnmarshalStrict(bytes, gvk)
joinBytes = bytes
}
if len(joinBytes) == 0 {
......
......@@ -280,23 +280,6 @@ func IsStandardFinalizerName(str string) bool {
return standardFinalizers.Has(str)
}
// AddToNodeAddresses appends the NodeAddresses to the passed-by-pointer slice,
// only if they do not already exist
func AddToNodeAddresses(addresses *[]core.NodeAddress, addAddresses ...core.NodeAddress) {
for _, add := range addAddresses {
exists := false
for _, existing := range *addresses {
if existing.Address == add.Address && existing.Type == add.Type {
exists = true
break
}
}
if !exists {
*addresses = append(*addresses, add)
}
}
}
// TODO: make method on LoadBalancerStatus?
func LoadBalancerStatusEqual(l, r *core.LoadBalancerStatus) bool {
return ingressSliceEqual(l.Ingress, r.Ingress)
......
......@@ -85,63 +85,6 @@ func TestIsStandardContainerResource(t *testing.T) {
}
}
func TestAddToNodeAddresses(t *testing.T) {
testCases := []struct {
existing []core.NodeAddress
toAdd []core.NodeAddress
expected []core.NodeAddress
}{
{
existing: []core.NodeAddress{},
toAdd: []core.NodeAddress{},
expected: []core.NodeAddress{},
},
{
existing: []core.NodeAddress{},
toAdd: []core.NodeAddress{
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
{Type: core.NodeHostName, Address: "localhost"},
},
expected: []core.NodeAddress{
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
{Type: core.NodeHostName, Address: "localhost"},
},
},
{
existing: []core.NodeAddress{},
toAdd: []core.NodeAddress{
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
},
expected: []core.NodeAddress{
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
},
},
{
existing: []core.NodeAddress{
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
{Type: core.NodeInternalIP, Address: "10.1.1.1"},
},
toAdd: []core.NodeAddress{
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
{Type: core.NodeHostName, Address: "localhost"},
},
expected: []core.NodeAddress{
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
{Type: core.NodeInternalIP, Address: "10.1.1.1"},
{Type: core.NodeHostName, Address: "localhost"},
},
},
}
for i, tc := range testCases {
AddToNodeAddresses(&tc.existing, tc.toAdd...)
if !Semantic.DeepEqual(tc.expected, tc.existing) {
t.Errorf("case[%d], expected: %v, got: %v", i, tc.expected, tc.existing)
}
}
}
func TestGetAccessModesFromString(t *testing.T) {
modes := GetAccessModesFromString("ROX")
if !containsAccessMode(modes, core.ReadOnlyMany) {
......
......@@ -108,23 +108,6 @@ func IsServiceIPSet(service *v1.Service) bool {
return service.Spec.ClusterIP != v1.ClusterIPNone && service.Spec.ClusterIP != ""
}
// AddToNodeAddresses appends the NodeAddresses to the passed-by-pointer slice,
// only if they do not already exist
func AddToNodeAddresses(addresses *[]v1.NodeAddress, addAddresses ...v1.NodeAddress) {
for _, add := range addAddresses {
exists := false
for _, existing := range *addresses {
if existing.Address == add.Address && existing.Type == add.Type {
exists = true
break
}
}
if !exists {
*addresses = append(*addresses, add)
}
}
}
// TODO: make method on LoadBalancerStatus?
func LoadBalancerStatusEqual(l, r *v1.LoadBalancerStatus) bool {
return ingressSliceEqual(l.Ingress, r.Ingress)
......
......@@ -147,63 +147,6 @@ func TestIsOvercommitAllowed(t *testing.T) {
}
}
func TestAddToNodeAddresses(t *testing.T) {
testCases := []struct {
existing []v1.NodeAddress
toAdd []v1.NodeAddress
expected []v1.NodeAddress
}{
{
existing: []v1.NodeAddress{},
toAdd: []v1.NodeAddress{},
expected: []v1.NodeAddress{},
},
{
existing: []v1.NodeAddress{},
toAdd: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeHostName, Address: "localhost"},
},
expected: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeHostName, Address: "localhost"},
},
},
{
existing: []v1.NodeAddress{},
toAdd: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
},
expected: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
},
},
{
existing: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
},
toAdd: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeHostName, Address: "localhost"},
},
expected: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeHostName, Address: "localhost"},
},
},
}
for i, tc := range testCases {
AddToNodeAddresses(&tc.existing, tc.toAdd...)
if !apiequality.Semantic.DeepEqual(tc.expected, tc.existing) {
t.Errorf("case[%d], expected: %v, got: %v", i, tc.expected, tc.existing)
}
}
}
func TestGetAccessModesFromString(t *testing.T) {
modes := GetAccessModesFromString("ROX")
if !containsAccessMode(modes, v1.ReadOnlyMany) {
......
......@@ -2632,18 +2632,11 @@ func validateRestartPolicy(restartPolicy *core.RestartPolicy, fldPath *field.Pat
func validateDNSPolicy(dnsPolicy *core.DNSPolicy, fldPath *field.Path) field.ErrorList {
allErrors := field.ErrorList{}
switch *dnsPolicy {
case core.DNSClusterFirstWithHostNet, core.DNSClusterFirst, core.DNSDefault:
case core.DNSNone:
if !utilfeature.DefaultFeatureGate.Enabled(features.CustomPodDNS) {
allErrors = append(allErrors, field.Invalid(fldPath, dnsPolicy, "DNSPolicy: can not use 'None', custom pod DNS is disabled by feature gate"))
}
case core.DNSClusterFirstWithHostNet, core.DNSClusterFirst, core.DNSDefault, core.DNSNone:
case "":
allErrors = append(allErrors, field.Required(fldPath, ""))
default:
validValues := []string{string(core.DNSClusterFirstWithHostNet), string(core.DNSClusterFirst), string(core.DNSDefault)}
if utilfeature.DefaultFeatureGate.Enabled(features.CustomPodDNS) {
validValues = append(validValues, string(core.DNSNone))
}
validValues := []string{string(core.DNSClusterFirstWithHostNet), string(core.DNSClusterFirst), string(core.DNSDefault), string(core.DNSNone)}
allErrors = append(allErrors, field.NotSupported(fldPath, dnsPolicy, validValues))
}
return allErrors
......@@ -2674,7 +2667,7 @@ func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolic
allErrs := field.ErrorList{}
// Validate DNSNone case. Must provide at least one DNS name server.
if utilfeature.DefaultFeatureGate.Enabled(features.CustomPodDNS) && dnsPolicy != nil && *dnsPolicy == core.DNSNone {
if dnsPolicy != nil && *dnsPolicy == core.DNSNone {
if dnsConfig == nil {
return append(allErrs, field.Required(fldPath, fmt.Sprintf("must provide `dnsConfig` when `dnsPolicy` is %s", core.DNSNone)))
}
......@@ -2684,10 +2677,6 @@ func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolic
}
if dnsConfig != nil {
if !utilfeature.DefaultFeatureGate.Enabled(features.CustomPodDNS) {
return append(allErrs, field.Forbidden(fldPath, "DNSConfig: custom pod DNS is disabled by feature gate"))
}
// Validate nameservers.
if len(dnsConfig.Nameservers) > MaxDNSNameservers {
allErrs = append(allErrs, field.Invalid(fldPath.Child("nameservers"), dnsConfig.Nameservers, fmt.Sprintf("must not have more than %v nameservers", MaxDNSNameservers)))
......
......@@ -5559,8 +5559,6 @@ func TestValidateRestartPolicy(t *testing.T) {
}
func TestValidateDNSPolicy(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CustomPodDNS, true)()
successCases := []core.DNSPolicy{core.DNSClusterFirst, core.DNSDefault, core.DNSPolicy(core.DNSClusterFirst), core.DNSNone}
for _, policy := range successCases {
if errs := validateDNSPolicy(&policy, field.NewPath("field")); len(errs) != 0 {
......@@ -5577,8 +5575,6 @@ func TestValidateDNSPolicy(t *testing.T) {
}
func TestValidatePodDNSConfig(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CustomPodDNS, true)()
generateTestSearchPathFunc := func(numChars int) string {
res := ""
for i := 0; i < numChars; i++ {
......
......@@ -3725,8 +3725,15 @@ func (c *Cloud) EnsureLoadBalancer(ctx context.Context, clusterName string, apiS
tcpHealthCheckPort = int32(*listener.InstancePort)
break
}
annotationProtocol := strings.ToLower(annotations[ServiceAnnotationLoadBalancerBEProtocol])
var hcProtocol string
if annotationProtocol == "https" || annotationProtocol == "ssl" {
hcProtocol = "SSL"
} else {
hcProtocol = "TCP"
}
// there must be no path on TCP health check
err = c.ensureLoadBalancerHealthCheck(loadBalancer, "TCP", tcpHealthCheckPort, "", annotations)
err = c.ensureLoadBalancerHealthCheck(loadBalancer, hcProtocol, tcpHealthCheckPort, "", annotations)
if err != nil {
return nil, err
}
......
......@@ -47,7 +47,6 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce",
deps = [
"//pkg/api/v1/service:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubelet/apis:go_default_library",
"//pkg/util/net/sets:go_default_library",
"//pkg/volume:go_default_library",
......@@ -73,6 +72,7 @@ go_library(
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
"//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library",
"//staging/src/k8s.io/cloud-provider:go_default_library",
"//staging/src/k8s.io/cloud-provider/features:go_default_library",
"//vendor/cloud.google.com/go/compute/metadata:go_default_library",
"//vendor/github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud:go_default_library",
"//vendor/github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/filter:go_default_library",
......
......@@ -38,8 +38,8 @@ import (
compute "google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
utilfeature "k8s.io/apiserver/pkg/util/feature"
cloudfeatures "k8s.io/cloud-provider/features"
"k8s.io/klog"
"k8s.io/kubernetes/pkg/features"
)
// DiskType defines a specific type for holding disk types (eg. pd-ssd)
......@@ -146,8 +146,8 @@ func (manager *gceServiceManager) CreateRegionalDiskOnCloudProvider(
diskType string,
replicaZones sets.String) error {
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
if !utilfeature.DefaultFeatureGate.Enabled(cloudfeatures.GCERegionalPersistentDisk) {
return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", cloudfeatures.GCERegionalPersistentDisk)
}
diskTypeURI, err := manager.getDiskTypeURI(
......@@ -247,8 +247,8 @@ func (manager *gceServiceManager) GetDiskFromCloudProvider(
func (manager *gceServiceManager) GetRegionalDiskFromCloudProvider(
diskName string) (*Disk, error) {
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
if !utilfeature.DefaultFeatureGate.Enabled(cloudfeatures.GCERegionalPersistentDisk) {
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", cloudfeatures.GCERegionalPersistentDisk)
}
ctx, cancel := cloud.ContextWithCallTimeout()
......@@ -284,8 +284,8 @@ func (manager *gceServiceManager) DeleteDiskOnCloudProvider(
func (manager *gceServiceManager) DeleteRegionalDiskOnCloudProvider(
diskName string) error {
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
if !utilfeature.DefaultFeatureGate.Enabled(cloudfeatures.GCERegionalPersistentDisk) {
return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", cloudfeatures.GCERegionalPersistentDisk)
}
ctx, cancel := cloud.ContextWithCallTimeout()
......@@ -411,8 +411,8 @@ func (manager *gceServiceManager) ResizeDiskOnCloudProvider(disk *Disk, sizeGb i
}
func (manager *gceServiceManager) RegionalResizeDiskOnCloudProvider(disk *Disk, sizeGb int64) error {
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
if !utilfeature.DefaultFeatureGate.Enabled(cloudfeatures.GCERegionalPersistentDisk) {
return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", cloudfeatures.GCERegionalPersistentDisk)
}
resizeServiceRequest := &compute.RegionDisksResizeRequest{
......@@ -532,7 +532,7 @@ func (g *Cloud) AttachDisk(diskName string, nodeName types.NodeName, readOnly bo
// Try fetching as regional PD
var disk *Disk
var mc *metricContext
if regional && utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
if regional && utilfeature.DefaultFeatureGate.Enabled(cloudfeatures.GCERegionalPersistentDisk) {
disk, err = g.getRegionalDiskByName(diskName)
if err != nil {
return err
......@@ -768,7 +768,7 @@ func (g *Cloud) ResizeDisk(diskToResize string, oldSize resource.Quantity, newSi
}
return newSizeQuant, mc.Observe(err)
case multiZone:
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
if !utilfeature.DefaultFeatureGate.Enabled(cloudfeatures.GCERegionalPersistentDisk) {
return oldSize, fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo)
}
......@@ -811,7 +811,7 @@ func (g *Cloud) GetAutoLabelsForPD(name string, zone string) (map[string]string,
// We could assume the disks exists; we have all the information we need
// However it is more consistent to ensure the disk exists,
// and in future we may gather addition information (e.g. disk type, IOPS etc)
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
if utilfeature.DefaultFeatureGate.Enabled(cloudfeatures.GCERegionalPersistentDisk) {
zoneSet, err := volumeutil.LabelZonesToSet(zone)
if err != nil {
klog.Warningf("Failed to parse zone field: %q. Will use raw field.", zone)
......@@ -916,7 +916,7 @@ func (g *Cloud) getRegionalDiskByName(diskName string) (*Disk, error) {
// Prefer getDiskByName, if the zone can be established
// Return cloudprovider.DiskNotFound if the given disk cannot be found in any zone
func (g *Cloud) GetDiskByNameUnknownZone(diskName string) (*Disk, error) {
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
if utilfeature.DefaultFeatureGate.Enabled(cloudfeatures.GCERegionalPersistentDisk) {
regionalDisk, err := g.getRegionalDiskByName(diskName)
if err == nil {
return regionalDisk, err
......@@ -996,7 +996,7 @@ func (g *Cloud) doDeleteDisk(diskToDelete string) error {
mc = newDiskMetricContextZonal("delete", disk.Region, zoneInfo.zone)
return mc.Observe(g.manager.DeleteDiskOnCloudProvider(zoneInfo.zone, disk.Name))
case multiZone:
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
if !utilfeature.DefaultFeatureGate.Enabled(cloudfeatures.GCERegionalPersistentDisk) {
return fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo)
}
......
......@@ -21,7 +21,6 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/openstack",
deps = [
"//pkg/api/v1/service:go_default_library",
"//pkg/apis/core/v1/helper:go_default_library",
"//pkg/kubelet/apis:go_default_library",
"//pkg/util/mount:go_default_library",
"//pkg/volume:go_default_library",
......@@ -34,6 +33,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
"//staging/src/k8s.io/cloud-provider:go_default_library",
"//staging/src/k8s.io/cloud-provider/node/helpers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions:go_default_library",
......
......@@ -46,8 +46,8 @@ import (
netutil "k8s.io/apimachinery/pkg/util/net"
certutil "k8s.io/client-go/util/cert"
cloudprovider "k8s.io/cloud-provider"
nodehelpers "k8s.io/cloud-provider/node/helpers"
"k8s.io/klog"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
)
const (
......@@ -476,7 +476,7 @@ func nodeAddresses(srv *servers.Server) ([]v1.NodeAddress, error) {
addressType = v1.NodeInternalIP
}
v1helper.AddToNodeAddresses(&addrs,
nodehelpers.AddToNodeAddresses(&addrs,
v1.NodeAddress{
Type: addressType,
Address: props.Addr,
......@@ -487,7 +487,7 @@ func nodeAddresses(srv *servers.Server) ([]v1.NodeAddress, error) {
// AccessIPs are usually duplicates of "public" addresses.
if srv.AccessIPv4 != "" {
v1helper.AddToNodeAddresses(&addrs,
nodehelpers.AddToNodeAddresses(&addrs,
v1.NodeAddress{
Type: v1.NodeExternalIP,
Address: srv.AccessIPv4,
......@@ -496,7 +496,7 @@ func nodeAddresses(srv *servers.Server) ([]v1.NodeAddress, error) {
}
if srv.AccessIPv6 != "" {
v1helper.AddToNodeAddresses(&addrs,
nodehelpers.AddToNodeAddresses(&addrs,
v1.NodeAddress{
Type: v1.NodeExternalIP,
Address: srv.AccessIPv6,
......@@ -505,7 +505,7 @@ func nodeAddresses(srv *servers.Server) ([]v1.NodeAddress, error) {
}
if srv.Metadata[TypeHostName] != "" {
v1helper.AddToNodeAddresses(&addrs,
nodehelpers.AddToNodeAddresses(&addrs,
v1.NodeAddress{
Type: v1.NodeHostName,
Address: srv.Metadata[TypeHostName],
......
......@@ -11,10 +11,10 @@ go_library(
srcs = ["photon.go"],
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/photon",
deps = [
"//pkg/apis/core/v1/helper:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/cloud-provider:go_default_library",
"//staging/src/k8s.io/cloud-provider/node/helpers:go_default_library",
"//vendor/github.com/vmware/photon-controller-go-sdk/photon:go_default_library",
"//vendor/gopkg.in/gcfg.v1:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
......
......@@ -39,8 +39,8 @@ import (
"k8s.io/api/core/v1"
k8stypes "k8s.io/apimachinery/pkg/types"
cloudprovider "k8s.io/cloud-provider"
nodehelpers "k8s.io/cloud-provider/node/helpers"
"k8s.io/klog"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
)
const (
......@@ -326,14 +326,14 @@ func (pc *PCCloud) NodeAddresses(ctx context.Context, nodeName k8stypes.NodeName
// Filter external IP by MAC address OUIs from vCenter and from ESX
if strings.HasPrefix(i.HardwareAddr.String(), MAC_OUI_VC) ||
strings.HasPrefix(i.HardwareAddr.String(), MAC_OUI_ESX) {
v1helper.AddToNodeAddresses(&nodeAddrs,
nodehelpers.AddToNodeAddresses(&nodeAddrs,
v1.NodeAddress{
Type: v1.NodeExternalIP,
Address: ipnet.IP.String(),
},
)
} else {
v1helper.AddToNodeAddresses(&nodeAddrs,
nodehelpers.AddToNodeAddresses(&nodeAddrs,
v1.NodeAddress{
Type: v1.NodeInternalIP,
Address: ipnet.IP.String(),
......@@ -396,14 +396,14 @@ func (pc *PCCloud) NodeAddresses(ctx context.Context, nodeName k8stypes.NodeName
if ipAddr != "-" {
if strings.HasPrefix(macAddr, MAC_OUI_VC) ||
strings.HasPrefix(macAddr, MAC_OUI_ESX) {
v1helper.AddToNodeAddresses(&nodeAddrs,
nodehelpers.AddToNodeAddresses(&nodeAddrs,
v1.NodeAddress{
Type: v1.NodeExternalIP,
Address: ipAddr,
},
)
} else {
v1helper.AddToNodeAddresses(&nodeAddrs,
nodehelpers.AddToNodeAddresses(&nodeAddrs,
v1.NodeAddress{
Type: v1.NodeInternalIP,
Address: ipAddr,
......
......@@ -16,7 +16,6 @@ go_library(
],
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere",
deps = [
"//pkg/apis/core/v1/helper:go_default_library",
"//pkg/cloudprovider/providers/vsphere/vclib:go_default_library",
"//pkg/cloudprovider/providers/vsphere/vclib/diskmanagers:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
......@@ -27,6 +26,7 @@ go_library(
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
"//staging/src/k8s.io/cloud-provider:go_default_library",
"//staging/src/k8s.io/cloud-provider/node/helpers:go_default_library",
"//vendor/github.com/vmware/govmomi/vapi/rest:go_default_library",
"//vendor/github.com/vmware/govmomi/vapi/tags:go_default_library",
"//vendor/github.com/vmware/govmomi/vim25:go_default_library",
......
......@@ -41,8 +41,8 @@ import (
"k8s.io/client-go/informers"
"k8s.io/client-go/tools/cache"
cloudprovider "k8s.io/cloud-provider"
nodehelpers "k8s.io/cloud-provider/node/helpers"
"k8s.io/klog"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere/vclib"
"k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere/vclib/diskmanagers"
)
......@@ -551,7 +551,7 @@ func getLocalIP() ([]v1.NodeAddress, error) {
var addressType v1.NodeAddressType
if strings.HasPrefix(i.HardwareAddr.String(), MacOuiVC) ||
strings.HasPrefix(i.HardwareAddr.String(), MacOuiEsx) {
v1helper.AddToNodeAddresses(&addrs,
nodehelpers.AddToNodeAddresses(&addrs,
v1.NodeAddress{
Type: v1.NodeExternalIP,
Address: ipnet.IP.String(),
......@@ -614,7 +614,7 @@ func (vs *VSphere) NodeAddresses(ctx context.Context, nodeName k8stypes.NodeName
return nil, err
}
// add the hostname address
v1helper.AddToNodeAddresses(&addrs, v1.NodeAddress{Type: v1.NodeHostName, Address: vs.hostName})
nodehelpers.AddToNodeAddresses(&addrs, v1.NodeAddress{Type: v1.NodeHostName, Address: vs.hostName})
return addrs, nil
}
......@@ -652,7 +652,7 @@ func (vs *VSphere) NodeAddresses(ctx context.Context, nodeName k8stypes.NodeName
if vs.cfg.Network.PublicNetwork == v.Network {
for _, ip := range v.IpAddress {
if net.ParseIP(ip).To4() != nil {
v1helper.AddToNodeAddresses(&addrs,
nodehelpers.AddToNodeAddresses(&addrs,
v1.NodeAddress{
Type: v1.NodeExternalIP,
Address: ip,
......
......@@ -432,7 +432,7 @@ type RealControllerRevisionControl struct {
var _ ControllerRevisionControlInterface = &RealControllerRevisionControl{}
func (r RealControllerRevisionControl) PatchControllerRevision(namespace, name string, data []byte) error {
_, err := r.KubeClient.AppsV1beta1().ControllerRevisions(namespace).Patch(name, types.StrategicMergePatchType, data)
_, err := r.KubeClient.AppsV1().ControllerRevisions(namespace).Patch(name, types.StrategicMergePatchType, data)
return err
}
......
......@@ -178,7 +178,7 @@ func (dsc *DaemonSetsController) cleanupHistory(ds *apps.DaemonSet, old []*apps.
continue
}
// Clean up
err := dsc.kubeClient.AppsV1beta1().ControllerRevisions(ds.Namespace).Delete(history.Name, nil)
err := dsc.kubeClient.AppsV1().ControllerRevisions(ds.Namespace).Delete(history.Name, nil)
if err != nil {
return err
}
......@@ -234,7 +234,7 @@ func (dsc *DaemonSetsController) dedupCurHistories(ds *apps.DaemonSet, curHistor
}
}
// Remove duplicates
err = dsc.kubeClient.AppsV1beta1().ControllerRevisions(ds.Namespace).Delete(cur.Name, nil)
err = dsc.kubeClient.AppsV1().ControllerRevisions(ds.Namespace).Delete(cur.Name, nil)
if err != nil {
return nil, err
}
......
......@@ -39,7 +39,6 @@ go_test(
srcs = ["daemonset_util_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/api/testapi:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubelet/apis:go_default_library",
"//pkg/scheduler/api:go_default_library",
......
......@@ -26,7 +26,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/features"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
......@@ -34,7 +33,7 @@ import (
func newPod(podName string, nodeName string, label map[string]string) *v1.Pod {
pod := &v1.Pod{
TypeMeta: metav1.TypeMeta{APIVersion: testapi.Extensions.GroupVersion().String()},
TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
ObjectMeta: metav1.ObjectMeta{
Labels: label,
Namespace: metav1.NamespaceDefault,
......
......@@ -430,6 +430,31 @@ func TestIntegerMaxUnavailableWithScaling(t *testing.T) {
ps.VerifyPdbStatus(t, pdbName, 0, 1, 3, 5, map[string]metav1.Time{})
}
// Verify that an percentage MaxUnavailable will recompute allowed disruptions when the scale of
// the selected pod's controller is modified.
func TestPercentageMaxUnavailableWithScaling(t *testing.T) {
dc, ps := newFakeDisruptionController()
pdb, pdbName := newMaxUnavailablePodDisruptionBudget(t, intstr.FromString("30%"))
add(t, dc.pdbStore, pdb)
rs, _ := newReplicaSet(t, 7)
add(t, dc.rsStore, rs)
pod, _ := newPod(t, "pod")
updatePodOwnerToRs(t, pod, rs)
add(t, dc.podStore, pod)
dc.sync(pdbName)
ps.VerifyPdbStatus(t, pdbName, 0, 1, 4, 7, map[string]metav1.Time{})
// Update scale of ReplicaSet and check PDB
rs.Spec.Replicas = to.Int32Ptr(3)
update(t, dc.rsStore, rs)
dc.sync(pdbName)
ps.VerifyPdbStatus(t, pdbName, 0, 1, 2, 3, map[string]metav1.Time{})
}
// Create a pod with no controller, and verify that a PDB with a percentage
// specified won't allow a disruption.
func TestNakedPod(t *testing.T) {
......
......@@ -13,6 +13,7 @@ go_library(
"//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/cloud-provider/features:go_default_library",
],
)
......
......@@ -20,6 +20,7 @@ import (
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
genericfeatures "k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
cloudfeatures "k8s.io/cloud-provider/features"
)
const (
......@@ -207,7 +208,7 @@ const (
CSINodeInfo utilfeature.Feature = "CSINodeInfo"
// owner @MrHohn
// beta: v1.10
// GA: v1.14
//
// Support configurable pod DNS parameters.
CustomPodDNS utilfeature.Feature = "CustomPodDNS"
......@@ -282,12 +283,6 @@ const (
// Enable container log rotation for cri container runtime
CRIContainerLogRotation utilfeature.Feature = "CRIContainerLogRotation"
// owner: @verult
// GA: v1.13
//
// Enables the regional PD feature on GCE.
GCERegionalPersistentDisk utilfeature.Feature = "GCERegionalPersistentDisk"
// owner: @krmayankk
// alpha: v1.10
//
......@@ -448,7 +443,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
CSIPersistentVolume: {Default: true, PreRelease: utilfeature.GA},
CSIDriverRegistry: {Default: false, PreRelease: utilfeature.Alpha},
CSINodeInfo: {Default: false, PreRelease: utilfeature.Alpha},
CustomPodDNS: {Default: true, PreRelease: utilfeature.Beta},
CustomPodDNS: {Default: true, PreRelease: utilfeature.GA, LockToDefault: true}, // remove in 1.16
BlockVolume: {Default: true, PreRelease: utilfeature.Beta},
StorageObjectInUseProtection: {Default: true, PreRelease: utilfeature.GA},
ResourceLimitsPriorityFunction: {Default: false, PreRelease: utilfeature.Alpha},
......@@ -460,7 +455,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
TokenRequestProjection: {Default: true, PreRelease: utilfeature.Beta},
BoundServiceAccountTokenVolume: {Default: false, PreRelease: utilfeature.Alpha},
CRIContainerLogRotation: {Default: true, PreRelease: utilfeature.Beta},
GCERegionalPersistentDisk: {Default: true, PreRelease: utilfeature.GA},
cloudfeatures.GCERegionalPersistentDisk: {Default: true, PreRelease: utilfeature.GA},
CSIMigration: {Default: false, PreRelease: utilfeature.Alpha},
CSIMigrationGCE: {Default: false, PreRelease: utilfeature.Alpha},
CSIMigrationAWS: {Default: false, PreRelease: utilfeature.Alpha},
......
......@@ -153,12 +153,6 @@ func (mk MergeKeys) GetMergeKeyValue(i interface{}) (MergeKeyValue, error) {
type source int
const (
recorded source = iota
local
remote
)
// CombinedPrimitiveSlice implements a slice of primitives
type CombinedPrimitiveSlice struct {
Items []*PrimitiveListItem
......
......@@ -293,7 +293,7 @@ var (
// NewDefaultKubectlCommand creates the `kubectl` command with default arguments
func NewDefaultKubectlCommand() *cobra.Command {
return NewDefaultKubectlCommandWithArgs(&defaultPluginHandler{}, os.Args, os.Stdin, os.Stdout, os.Stderr)
return NewDefaultKubectlCommandWithArgs(NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes), os.Args, os.Stdin, os.Stdout, os.Stderr)
}
// NewDefaultKubectlCommandWithArgs creates the `kubectl` command with arguments
......@@ -310,7 +310,7 @@ func NewDefaultKubectlCommandWithArgs(pluginHandler PluginHandler, args []string
// only look for suitable extension executables if
// the specified command does not already exist
if _, _, err := cmd.Find(cmdPathPieces); err != nil {
if err := handleEndpointExtensions(pluginHandler, cmdPathPieces); err != nil {
if err := HandlePluginCommand(pluginHandler, cmdPathPieces); err != nil {
fmt.Fprintf(errout, "%v\n", err)
os.Exit(1)
}
......@@ -324,29 +324,51 @@ func NewDefaultKubectlCommandWithArgs(pluginHandler PluginHandler, args []string
// and performing executable filename lookups to search
// for valid plugin files, and execute found plugins.
type PluginHandler interface {
// Lookup receives a potential filename and returns
// a full or relative path to an executable, if one
// exists at the given filename, or an error.
Lookup(filename string) (string, error)
// exists at the given filename, or a boolean false.
// Lookup will iterate over a list of given prefixes
// in order to recognize valid plugin filenames.
// The first filepath to match a prefix is returned.
Lookup(filename string) (string, bool)
// Execute receives an executable's filepath, a slice
// of arguments, and a slice of environment variables
// to relay to the executable.
Execute(executablePath string, cmdArgs, environment []string) error
}
type defaultPluginHandler struct{}
// DefaultPluginHandler implements PluginHandler
type DefaultPluginHandler struct {
ValidPrefixes []string
}
// NewDefaultPluginHandler instantiates the DefaultPluginHandler with a list of
// given filename prefixes used to identify valid plugin filenames.
func NewDefaultPluginHandler(validPrefixes []string) *DefaultPluginHandler {
return &DefaultPluginHandler{
ValidPrefixes: validPrefixes,
}
}
// Lookup implements PluginHandler
func (h *defaultPluginHandler) Lookup(filename string) (string, error) {
return exec.LookPath(filename)
func (h *DefaultPluginHandler) Lookup(filename string) (string, bool) {
for _, prefix := range h.ValidPrefixes {
path, err := exec.LookPath(fmt.Sprintf("%s-%s", prefix, filename))
if err != nil || len(path) == 0 {
continue
}
return path, true
}
return "", false
}
// Execute implements PluginHandler
func (h *defaultPluginHandler) Execute(executablePath string, cmdArgs, environment []string) error {
func (h *DefaultPluginHandler) Execute(executablePath string, cmdArgs, environment []string) error {
return syscall.Exec(executablePath, cmdArgs, environment)
}
func handleEndpointExtensions(pluginHandler PluginHandler, cmdArgs []string) error {
// HandlePluginCommand receives a pluginHandler and command-line arguments and attempts to find
// a plugin executable on the PATH that satisfies the given arguments.
func HandlePluginCommand(pluginHandler PluginHandler, cmdArgs []string) error {
remainingArgs := []string{} // all "non-flag" arguments
for idx := range cmdArgs {
......@@ -360,8 +382,8 @@ func handleEndpointExtensions(pluginHandler PluginHandler, cmdArgs []string) err
// attempt to find binary, starting at longest possible name with given cmdArgs
for len(remainingArgs) > 0 {
path, err := pluginHandler.Lookup(fmt.Sprintf("kubectl-%s", strings.Join(remainingArgs, "-")))
if err != nil || len(path) == 0 {
path, found := pluginHandler.Lookup(strings.Join(remainingArgs, "-"))
if !found {
remainingArgs = remainingArgs[:len(remainingArgs)-1]
continue
}
......
......@@ -175,32 +175,35 @@ type testPluginHandler struct {
err error
}
func (h *testPluginHandler) Lookup(filename string) (string, error) {
func (h *testPluginHandler) Lookup(filename string) (string, bool) {
// append supported plugin prefix to the filename
filename = fmt.Sprintf("%s-%s", "kubectl", filename)
dir, err := os.Stat(h.pluginsDirectory)
if err != nil {
h.err = err
return "", err
return "", false
}
if !dir.IsDir() {
h.err = fmt.Errorf("expected %q to be a directory", h.pluginsDirectory)
return "", h.err
return "", false
}
plugins, err := ioutil.ReadDir(h.pluginsDirectory)
if err != nil {
h.err = err
return "", err
return "", false
}
for _, p := range plugins {
if p.Name() == filename {
return fmt.Sprintf("%s/%s", h.pluginsDirectory, p.Name()), nil
return fmt.Sprintf("%s/%s", h.pluginsDirectory, p.Name()), true
}
}
h.err = fmt.Errorf("unable to find a plugin executable %q", filename)
return "", h.err
return "", false
}
func (h *testPluginHandler) Execute(executablePath string, cmdArgs, env []string) error {
......
......@@ -49,6 +49,8 @@ var (
- anywhere on the user's PATH
- begin with "kubectl-"
`)
ValidPluginFilenamePrefixes = []string{"kubectl"}
)
func NewCmdPlugin(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
......@@ -127,12 +129,12 @@ func (o *PluginListOptions) Run() error {
if f.IsDir() {
continue
}
if !strings.HasPrefix(f.Name(), "kubectl-") {
if !hasValidPrefix(f.Name(), ValidPluginFilenamePrefixes) {
continue
}
if isFirstFile {
fmt.Fprintf(o.ErrOut, "The following kubectl-compatible plugins are available:\n\n")
fmt.Fprintf(o.ErrOut, "The following compatible plugins are available:\n\n")
pluginsFound = true
isFirstFile = false
}
......@@ -262,3 +264,13 @@ func uniquePathsList(paths []string) []string {
}
return newPaths
}
func hasValidPrefix(filepath string, validPrefixes []string) bool {
for _, prefix := range validPrefixes {
if !strings.HasPrefix(filepath, prefix+"-") {
continue
}
return true
}
return false
}
......@@ -21,6 +21,7 @@ go_test(
deps = [
"//pkg/cloudprovider/providers/fake:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
],
)
......
......@@ -30,8 +30,6 @@ import (
"k8s.io/klog"
)
var nodeAddressesRetryPeriod = 5 * time.Second
// SyncManager is an interface for making requests to a cloud provider
type SyncManager interface {
Run(stopCh <-chan struct{})
......@@ -46,79 +44,92 @@ type cloudResourceSyncManager struct {
// Sync period
syncPeriod time.Duration
nodeAddressesMux sync.Mutex
nodeAddressesErr error
nodeAddresses []v1.NodeAddress
nodeAddressesMonitor *sync.Cond
nodeAddressesErr error
nodeAddresses []v1.NodeAddress
nodeName types.NodeName
}
// NewSyncManager creates a manager responsible for collecting resources
// from a cloud provider through requests that are sensitive to timeouts and hanging
// NewSyncManager creates a manager responsible for collecting resources from a
// cloud provider through requests that are sensitive to timeouts and hanging
func NewSyncManager(cloud cloudprovider.Interface, nodeName types.NodeName, syncPeriod time.Duration) SyncManager {
return &cloudResourceSyncManager{
cloud: cloud,
syncPeriod: syncPeriod,
nodeName: nodeName,
// nodeAddressesMonitor is a monitor that guards a result (nodeAddresses,
// nodeAddressesErr) of the sync loop under the condition that a result has
// been saved at least once. The semantics here are:
//
// * Readers of the result will wait on the monitor until the first result
// has been saved.
// * The sync loop (i.e. the only writer), will signal all waiters every
// time it updates the result.
nodeAddressesMonitor: sync.NewCond(&sync.Mutex{}),
}
}
func (manager *cloudResourceSyncManager) getNodeAddressSafe() ([]v1.NodeAddress, error) {
manager.nodeAddressesMux.Lock()
defer manager.nodeAddressesMux.Unlock()
return manager.nodeAddresses, manager.nodeAddressesErr
}
func (manager *cloudResourceSyncManager) setNodeAddressSafe(nodeAddresses []v1.NodeAddress, err error) {
manager.nodeAddressesMux.Lock()
defer manager.nodeAddressesMux.Unlock()
manager.nodeAddresses = nodeAddresses
manager.nodeAddressesErr = err
}
// NodeAddresses does not wait for cloud provider to return a node addresses.
// It always returns node addresses or an error.
func (manager *cloudResourceSyncManager) NodeAddresses() ([]v1.NodeAddress, error) {
// NodeAddresses waits for the first sync loop to run. If no successful syncs
// have run, it will return the most recent error. If node addresses have been
// synced successfully, it will return the list of node addresses from the most
// recent successful sync.
func (m *cloudResourceSyncManager) NodeAddresses() ([]v1.NodeAddress, error) {
m.nodeAddressesMonitor.L.Lock()
defer m.nodeAddressesMonitor.L.Unlock()
// wait until there is something
for {
nodeAddresses, err := manager.getNodeAddressSafe()
if len(nodeAddresses) == 0 && err == nil {
klog.V(5).Infof("Waiting for %v for cloud provider to provide node addresses", nodeAddressesRetryPeriod)
time.Sleep(nodeAddressesRetryPeriod)
continue
if addrs, err := m.nodeAddresses, m.nodeAddressesErr; len(addrs) > 0 || err != nil {
return addrs, err
}
return nodeAddresses, err
klog.V(5).Infof("Waiting for cloud provider to provide node addresses")
m.nodeAddressesMonitor.Wait()
}
}
func (manager *cloudResourceSyncManager) collectNodeAddresses(ctx context.Context, nodeName types.NodeName) {
klog.V(5).Infof("Requesting node addresses from cloud provider for node %q", nodeName)
instances, ok := manager.cloud.Instances()
// getNodeAddresses calls the cloud provider to get a current list of node addresses.
func (m *cloudResourceSyncManager) getNodeAddresses() ([]v1.NodeAddress, error) {
// TODO(roberthbailey): Can we do this without having credentials to talk to
// the cloud provider?
// TODO(justinsb): We can if CurrentNodeName() was actually CurrentNode() and
// returned an interface.
// TODO: If IP addresses couldn't be fetched from the cloud provider, should
// kubelet fallback on the other methods for getting the IP below?
instances, ok := m.cloud.Instances()
if !ok {
manager.setNodeAddressSafe(nil, fmt.Errorf("failed to get instances from cloud provider"))
return
return nil, fmt.Errorf("failed to get instances from cloud provider")
}
return instances.NodeAddresses(context.TODO(), m.nodeName)
}
func (m *cloudResourceSyncManager) syncNodeAddresses() {
klog.V(5).Infof("Requesting node addresses from cloud provider for node %q", m.nodeName)
// TODO(roberthbailey): Can we do this without having credentials to talk
// to the cloud provider?
// TODO(justinsb): We can if CurrentNodeName() was actually CurrentNode() and returned an interface
// TODO: If IP addresses couldn't be fetched from the cloud provider, should kubelet fallback on the other methods for getting the IP below?
addrs, err := m.getNodeAddresses()
m.nodeAddressesMonitor.L.Lock()
defer m.nodeAddressesMonitor.L.Unlock()
defer m.nodeAddressesMonitor.Broadcast()
nodeAddresses, err := instances.NodeAddresses(ctx, nodeName)
if err != nil {
manager.setNodeAddressSafe(nil, fmt.Errorf("failed to get node address from cloud provider: %v", err))
klog.V(2).Infof("Node addresses from cloud provider for node %q not collected", nodeName)
} else {
manager.setNodeAddressSafe(nodeAddresses, nil)
klog.V(5).Infof("Node addresses from cloud provider for node %q collected", nodeName)
klog.V(2).Infof("Node addresses from cloud provider for node %q not collected: %v", m.nodeName, err)
if len(m.nodeAddresses) > 0 {
// in the event that a sync loop fails when a previous sync had
// succeeded, continue to use the old addresses.
return
}
m.nodeAddressesErr = fmt.Errorf("failed to get node address from cloud provider: %v", err)
return
}
klog.V(5).Infof("Node addresses from cloud provider for node %q collected", m.nodeName)
m.nodeAddressesErr = nil
m.nodeAddresses = addrs
}
func (manager *cloudResourceSyncManager) Run(stopCh <-chan struct{}) {
wait.Until(func() {
manager.collectNodeAddresses(context.TODO(), manager.nodeName)
}, manager.syncPeriod, stopCh)
// Run starts the cloud resource sync manager's sync loop.
func (m *cloudResourceSyncManager) Run(stopCh <-chan struct{}) {
wait.Until(m.syncNodeAddresses, m.syncPeriod, stopCh)
}
......@@ -17,33 +17,16 @@ limitations under the License.
package cloudresource
import (
"fmt"
"errors"
"reflect"
"testing"
"time"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/kubernetes/pkg/cloudprovider/providers/fake"
)
func collectNodeAddresses(manager *cloudResourceSyncManager) ([]v1.NodeAddress, error) {
var nodeAddresses []v1.NodeAddress
var err error
collected := make(chan struct{}, 1)
go func() {
nodeAddresses, err = manager.NodeAddresses()
close(collected)
}()
select {
case <-collected:
return nodeAddresses, err
case <-time.Tick(2 * nodeAddressesRetryPeriod):
return nil, fmt.Errorf("Timeout after %v waiting for address to appear", 2*nodeAddressesRetryPeriod)
}
}
func createNodeInternalIPAddress(address string) []v1.NodeAddress {
return []v1.NodeAddress{
{
......@@ -53,13 +36,12 @@ func createNodeInternalIPAddress(address string) []v1.NodeAddress {
}
}
func TestNodeAddressesRequest(t *testing.T) {
syncPeriod := 300 * time.Millisecond
maxRetry := 5
func TestNodeAddressesDelay(t *testing.T) {
syncPeriod := 100 * time.Millisecond
cloud := &fake.FakeCloud{
Addresses: createNodeInternalIPAddress("10.0.1.12"),
// Set the request delay so the manager timeouts and collects the node addresses later
RequestDelay: 400 * time.Millisecond,
RequestDelay: 200 * time.Millisecond,
}
stopCh := make(chan struct{})
defer close(stopCh)
......@@ -67,27 +49,28 @@ func TestNodeAddressesRequest(t *testing.T) {
manager := NewSyncManager(cloud, "defaultNode", syncPeriod).(*cloudResourceSyncManager)
go manager.Run(stopCh)
nodeAddresses, err := collectNodeAddresses(manager)
nodeAddresses, err := manager.NodeAddresses()
if err != nil {
t.Errorf("Unexpected err: %q\n", err)
}
if !reflect.DeepEqual(nodeAddresses, cloud.Addresses) {
t.Errorf("Unexpected list of node addresses %#v, expected %#v: %v", nodeAddresses, cloud.Addresses, err)
t.Errorf("Unexpected diff of node addresses: %v", diff.ObjectReflectDiff(nodeAddresses, cloud.Addresses))
}
// Change the IP address
cloud.SetNodeAddresses(createNodeInternalIPAddress("10.0.1.13"))
// Wait until the IP address changes
maxRetry := 5
for i := 0; i < maxRetry; i++ {
nodeAddresses, err := collectNodeAddresses(manager)
nodeAddresses, err := manager.NodeAddresses()
t.Logf("nodeAddresses: %#v, err: %v", nodeAddresses, err)
if err != nil {
t.Errorf("Unexpected err: %q\n", err)
}
// It is safe to read cloud.Addresses since no routine is changing the value at the same time
if err == nil && nodeAddresses[0].Address != cloud.Addresses[0].Address {
time.Sleep(100 * time.Millisecond)
time.Sleep(syncPeriod)
continue
}
if err != nil {
......@@ -97,3 +80,54 @@ func TestNodeAddressesRequest(t *testing.T) {
}
t.Errorf("Timeout waiting for %q address to appear", cloud.Addresses[0].Address)
}
func TestNodeAddressesUsesLastSuccess(t *testing.T) {
cloud := &fake.FakeCloud{}
manager := NewSyncManager(cloud, "defaultNode", 0).(*cloudResourceSyncManager)
// These tests are stateful and order dependant.
tests := []struct {
name string
addrs []v1.NodeAddress
err error
wantAddrs []v1.NodeAddress
wantErr bool
}{
{
name: "first sync loop encounters an error",
err: errors.New("bad"),
wantErr: true,
},
{
name: "subsequent sync loop succeeds",
addrs: createNodeInternalIPAddress("10.0.1.12"),
wantAddrs: createNodeInternalIPAddress("10.0.1.12"),
},
{
name: "subsequent sync loop encounters an error, last addresses returned",
err: errors.New("bad"),
wantAddrs: createNodeInternalIPAddress("10.0.1.12"),
},
{
name: "subsequent sync loop succeeds changing addresses",
addrs: createNodeInternalIPAddress("10.0.1.13"),
wantAddrs: createNodeInternalIPAddress("10.0.1.13"),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cloud.Addresses = test.addrs
cloud.Err = test.err
manager.syncNodeAddresses()
nodeAddresses, err := manager.NodeAddresses()
if (err != nil) != test.wantErr {
t.Errorf("unexpected err: %v", err)
}
if got, want := nodeAddresses, test.wantAddrs; !reflect.DeepEqual(got, want) {
t.Errorf("Unexpected diff of node addresses: %v", diff.ObjectReflectDiff(got, want))
}
})
}
}
......@@ -88,7 +88,7 @@ type Runtime interface {
// TODO: Revisit this method and make it cleaner.
GarbageCollect(gcPolicy ContainerGCPolicy, allSourcesReady bool, evictNonDeletedPods bool) error
// Syncs the running pod into the desired pod.
SyncPod(pod *v1.Pod, apiPodStatus v1.PodStatus, podStatus *PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) PodSyncResult
SyncPod(pod *v1.Pod, podStatus *PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) PodSyncResult
// KillPod kills all the containers of a pod. Pod may be nil, running pod must not be.
// TODO(random-liu): Return PodSyncResult in KillPod.
// gracePeriodOverride if specified allows the caller to override the pod default grace period.
......@@ -155,7 +155,7 @@ type ContainerAttacher interface {
type ContainerCommandRunner interface {
// RunInContainer synchronously executes the command in the container, and returns the output.
// If the command completes with a non-0 exit code, a pkg/util/exec.ExitError will be returned.
// If the command completes with a non-0 exit code, a k8s.io/utils/exec.ExitError will be returned.
RunInContainer(id ContainerID, cmd []string, timeout time.Duration) ([]byte, error)
}
......
......@@ -220,7 +220,7 @@ func (f *FakeRuntime) GetPods(all bool) ([]*Pod, error) {
return pods, f.Err
}
func (f *FakeRuntime) SyncPod(pod *v1.Pod, _ v1.PodStatus, _ *PodStatus, _ []v1.Secret, backOff *flowcontrol.Backoff) (result PodSyncResult) {
func (f *FakeRuntime) SyncPod(pod *v1.Pod, _ *PodStatus, _ []v1.Secret, backOff *flowcontrol.Backoff) (result PodSyncResult) {
f.Lock()
defer f.Unlock()
......
......@@ -67,8 +67,8 @@ func (r *Mock) GetPods(all bool) ([]*Pod, error) {
return args.Get(0).([]*Pod), args.Error(1)
}
func (r *Mock) SyncPod(pod *v1.Pod, apiStatus v1.PodStatus, status *PodStatus, secrets []v1.Secret, backOff *flowcontrol.Backoff) PodSyncResult {
args := r.Called(pod, apiStatus, status, secrets, backOff)
func (r *Mock) SyncPod(pod *v1.Pod, status *PodStatus, secrets []v1.Secret, backOff *flowcontrol.Backoff) PodSyncResult {
args := r.Called(pod, status, secrets, backOff)
return args.Get(0).(PodSyncResult)
}
......
......@@ -1660,7 +1660,7 @@ func (kl *Kubelet) syncPod(o syncPodOptions) error {
pullSecrets := kl.getPullSecretsForPod(pod)
// Call the container runtime's SyncPod callback
result := kl.containerRuntime.SyncPod(pod, apiPodStatus, podStatus, pullSecrets, kl.backOff)
result := kl.containerRuntime.SyncPod(pod, podStatus, pullSecrets, kl.backOff)
kl.reasonCache.Update(pod.UID, result)
if err := result.Error(); err != nil {
// Do not return error if the only failures were pods in backoff
......
......@@ -592,7 +592,7 @@ func (m *kubeGenericRuntimeManager) computePodActions(pod *v1.Pod, podStatus *ku
// 4. Create sandbox if necessary.
// 5. Create init containers.
// 6. Create normal containers.
func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, _ v1.PodStatus, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {
func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {
// Step 1: Compute sandbox and container changes.
podContainerChanges := m.computePodActions(pod, podStatus)
klog.V(3).Infof("computePodActions got %+v for pod %q", podContainerChanges, format.Pod(pod))
......
......@@ -566,7 +566,7 @@ func TestSyncPod(t *testing.T) {
}
backOff := flowcontrol.NewBackOff(time.Second, time.Minute)
result := m.SyncPod(pod, v1.PodStatus{}, &kubecontainer.PodStatus{}, []v1.Secret{}, backOff)
result := m.SyncPod(pod, &kubecontainer.PodStatus{}, []v1.Secret{}, backOff)
assert.NoError(t, result.Error())
assert.Equal(t, 2, len(fakeRuntime.Containers))
assert.Equal(t, 2, len(fakeImage.Images))
......@@ -655,7 +655,7 @@ func TestSyncPodWithInitContainers(t *testing.T) {
// 1. should only create the init container.
podStatus, err := m.GetPodStatus(pod.UID, pod.Name, pod.Namespace)
assert.NoError(t, err)
result := m.SyncPod(pod, v1.PodStatus{}, podStatus, []v1.Secret{}, backOff)
result := m.SyncPod(pod, podStatus, []v1.Secret{}, backOff)
assert.NoError(t, result.Error())
expected := []*cRecord{
{name: initContainers[0].Name, attempt: 0, state: runtimeapi.ContainerState_CONTAINER_RUNNING},
......@@ -665,7 +665,7 @@ func TestSyncPodWithInitContainers(t *testing.T) {
// 2. should not create app container because init container is still running.
podStatus, err = m.GetPodStatus(pod.UID, pod.Name, pod.Namespace)
assert.NoError(t, err)
result = m.SyncPod(pod, v1.PodStatus{}, podStatus, []v1.Secret{}, backOff)
result = m.SyncPod(pod, podStatus, []v1.Secret{}, backOff)
assert.NoError(t, result.Error())
verifyContainerStatuses(t, fakeRuntime, expected, "init container still running; do nothing")
......@@ -680,7 +680,7 @@ func TestSyncPodWithInitContainers(t *testing.T) {
// Sync again.
podStatus, err = m.GetPodStatus(pod.UID, pod.Name, pod.Namespace)
assert.NoError(t, err)
result = m.SyncPod(pod, v1.PodStatus{}, podStatus, []v1.Secret{}, backOff)
result = m.SyncPod(pod, podStatus, []v1.Secret{}, backOff)
assert.NoError(t, result.Error())
expected = []*cRecord{
{name: initContainers[0].Name, attempt: 0, state: runtimeapi.ContainerState_CONTAINER_EXITED},
......@@ -695,7 +695,7 @@ func TestSyncPodWithInitContainers(t *testing.T) {
// Sync again.
podStatus, err = m.GetPodStatus(pod.UID, pod.Name, pod.Namespace)
assert.NoError(t, err)
result = m.SyncPod(pod, v1.PodStatus{}, podStatus, []v1.Secret{}, backOff)
result = m.SyncPod(pod, podStatus, []v1.Secret{}, backOff)
assert.NoError(t, result.Error())
expected = []*cRecord{
// The first init container instance is purged and no longer visible.
......
......@@ -7,13 +7,11 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/core/validation:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubelet/apis/cri/runtime/v1alpha2:go_default_library",
"//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/util/format:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
......@@ -24,14 +22,11 @@ go_test(
srcs = ["dns_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/features:go_default_library",
"//pkg/kubelet/apis/cri/runtime/v1alpha2:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
......
......@@ -27,10 +27,8 @@ import (
"k8s.io/api/core/v1"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/util/format"
......@@ -265,12 +263,7 @@ func getPodDNSType(pod *v1.Pod) (podDNSType, error) {
dnsPolicy := pod.Spec.DNSPolicy
switch dnsPolicy {
case v1.DNSNone:
if utilfeature.DefaultFeatureGate.Enabled(features.CustomPodDNS) {
return podDNSNone, nil
}
// This should not happen as kube-apiserver should have rejected
// setting dnsPolicy to DNSNone when feature gate is disabled.
return podDNSCluster, fmt.Errorf(fmt.Sprintf("invalid DNSPolicy=%v: custom pod DNS is disabled", dnsPolicy))
return podDNSNone, nil
case v1.DNSClusterFirstWithHostNet:
return podDNSCluster, nil
case v1.DNSClusterFirst:
......@@ -383,7 +376,7 @@ func (c *Configurer) GetPodDNS(pod *v1.Pod) (*runtimeapi.DNSConfig, error) {
}
}
if utilfeature.DefaultFeatureGate.Enabled(features.CustomPodDNS) && pod.Spec.DNSConfig != nil {
if pod.Spec.DNSConfig != nil {
dnsConfig = appendDNSConfig(dnsConfig, pod.Spec.DNSConfig)
}
return c.formDNSConfigFitsLimits(dnsConfig, pod), nil
......
......@@ -28,10 +28,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/features"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
"github.com/stretchr/testify/assert"
......@@ -291,13 +288,12 @@ func TestGetPodDNSType(t *testing.T) {
}
testCases := []struct {
desc string
customPodDNSFeatureGate bool
hasClusterDNS bool
hostNetwork bool
dnsPolicy v1.DNSPolicy
expectedDNSType podDNSType
expectedError bool
desc string
hasClusterDNS bool
hostNetwork bool
dnsPolicy v1.DNSPolicy
expectedDNSType podDNSType
expectedError bool
}{
{
desc: "valid DNSClusterFirst without hostnetwork",
......@@ -337,15 +333,9 @@ func TestGetPodDNSType(t *testing.T) {
expectedDNSType: podDNSHost,
},
{
desc: "valid DNSNone with feature gate",
customPodDNSFeatureGate: true,
dnsPolicy: v1.DNSNone,
expectedDNSType: podDNSNone,
},
{
desc: "DNSNone without feature gate, should return error",
dnsPolicy: v1.DNSNone,
expectedError: true,
desc: "valid DNSNone",
dnsPolicy: v1.DNSNone,
expectedDNSType: podDNSNone,
},
{
desc: "invalid DNS policy, should return error",
......@@ -356,8 +346,6 @@ func TestGetPodDNSType(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CustomPodDNS, tc.customPodDNSFeatureGate)()
if tc.hasClusterDNS {
configurer.clusterDNS = testClusterDNS
} else {
......@@ -516,32 +504,20 @@ func TestGetPodDNSCustom(t *testing.T) {
configurer := NewConfigurer(recorder, nodeRef, nil, []net.IP{net.ParseIP(testClusterNameserver)}, testClusterDNSDomain, tmpfile.Name())
testCases := []struct {
desc string
customPodDNSFeatureGate bool
hostnetwork bool
dnsPolicy v1.DNSPolicy
dnsConfig *v1.PodDNSConfig
expectedDNSConfig *runtimeapi.DNSConfig
desc string
hostnetwork bool
dnsPolicy v1.DNSPolicy
dnsConfig *v1.PodDNSConfig
expectedDNSConfig *runtimeapi.DNSConfig
}{
{
desc: "feature gate is disabled, DNSNone should fallback to DNSClusterFirst",
dnsPolicy: v1.DNSNone,
expectedDNSConfig: &runtimeapi.DNSConfig{
Servers: []string{testClusterNameserver},
Searches: []string{testNsSvcDomain, testSvcDomain, testClusterDNSDomain, testHostDomain},
Options: []string{"ndots:5"},
},
},
{
desc: "feature gate is enabled, DNSNone without DNSConfig should have empty DNS settings",
customPodDNSFeatureGate: true,
dnsPolicy: v1.DNSNone,
expectedDNSConfig: &runtimeapi.DNSConfig{},
desc: "DNSNone without DNSConfig should have empty DNS settings",
dnsPolicy: v1.DNSNone,
expectedDNSConfig: &runtimeapi.DNSConfig{},
},
{
desc: "feature gate is enabled, DNSNone with DNSConfig should have a merged DNS settings",
customPodDNSFeatureGate: true,
dnsPolicy: v1.DNSNone,
desc: "DNSNone with DNSConfig should have a merged DNS settings",
dnsPolicy: v1.DNSNone,
dnsConfig: &v1.PodDNSConfig{
Nameservers: []string{"203.0.113.1"},
Searches: []string{"my.domain", "second.domain"},
......@@ -557,9 +533,8 @@ func TestGetPodDNSCustom(t *testing.T) {
},
},
{
desc: "feature gate is enabled, DNSClusterFirst with DNSConfig should have a merged DNS settings",
customPodDNSFeatureGate: true,
dnsPolicy: v1.DNSClusterFirst,
desc: "DNSClusterFirst with DNSConfig should have a merged DNS settings",
dnsPolicy: v1.DNSClusterFirst,
dnsConfig: &v1.PodDNSConfig{
Nameservers: []string{"10.0.0.11"},
Searches: []string{"my.domain"},
......@@ -575,10 +550,9 @@ func TestGetPodDNSCustom(t *testing.T) {
},
},
{
desc: "feature gate is enabled, DNSClusterFirstWithHostNet with DNSConfig should have a merged DNS settings",
customPodDNSFeatureGate: true,
hostnetwork: true,
dnsPolicy: v1.DNSClusterFirstWithHostNet,
desc: "DNSClusterFirstWithHostNet with DNSConfig should have a merged DNS settings",
hostnetwork: true,
dnsPolicy: v1.DNSClusterFirstWithHostNet,
dnsConfig: &v1.PodDNSConfig{
Nameservers: []string{"10.0.0.11"},
Searches: []string{"my.domain"},
......@@ -594,9 +568,8 @@ func TestGetPodDNSCustom(t *testing.T) {
},
},
{
desc: "feature gate is enabled, DNSDefault with DNSConfig should have a merged DNS settings",
customPodDNSFeatureGate: true,
dnsPolicy: v1.DNSDefault,
desc: "DNSDefault with DNSConfig should have a merged DNS settings",
dnsPolicy: v1.DNSDefault,
dnsConfig: &v1.PodDNSConfig{
Nameservers: []string{"10.0.0.11"},
Searches: []string{"my.domain"},
......@@ -615,8 +588,6 @@ func TestGetPodDNSCustom(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CustomPodDNS, tc.customPodDNSFeatureGate)()
testPod.Spec.HostNetwork = tc.hostnetwork
testPod.Spec.DNSConfig = tc.dnsConfig
testPod.Spec.DNSPolicy = tc.dnsPolicy
......
......@@ -143,7 +143,7 @@ type Subpath struct {
// Exec executes command where mount utilities are. This can be either the host,
// container where kubelet runs or even a remote pod with mount utilities.
// Usual pkg/util/exec interface is not used because kubelet.RunInContainer does
// Usual k8s.io/utils/exec interface is not used because kubelet.RunInContainer does
// not provide stdin/stdout/stderr streams.
type Exec interface {
// Run executes a command and returns its stdout + stderr combined in one
......
......@@ -31,6 +31,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/cloud-provider:go_default_library",
"//staging/src/k8s.io/cloud-provider/features:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/path:go_default_library",
......
......@@ -28,9 +28,9 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
utilfeature "k8s.io/apiserver/pkg/util/feature"
cloudprovider "k8s.io/cloud-provider"
cloudfeatures "k8s.io/cloud-provider/features"
"k8s.io/klog"
gcecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
"k8s.io/kubernetes/pkg/features"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/volume"
......@@ -127,10 +127,10 @@ func (util *GCEDiskUtil) CreateVolume(c *gcePersistentDiskProvisioner, node *v1.
return "", 0, nil, "", err
}
case "replication-type":
if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
if !utilfeature.DefaultFeatureGate.Enabled(cloudfeatures.GCERegionalPersistentDisk) {
return "", 0, nil, "",
fmt.Errorf("the %q option for volume plugin %v is only supported with the %q Kubernetes feature gate enabled",
k, c.plugin.GetPluginName(), features.GCERegionalPersistentDisk)
k, c.plugin.GetPluginName(), cloudfeatures.GCERegionalPersistentDisk)
}
replicationType = strings.ToLower(v)
case volume.VolumeParameterFSType:
......
......@@ -188,6 +188,7 @@
allowedImports:
- k8s.io/api
- k8s.io/apimachinery
- k8s.io/apiserver
- k8s.io/client-go
- k8s.io/klog
......
......@@ -3156,6 +3156,7 @@ message PodSpec {
// EnableServiceLinks indicates whether information about services should be injected into pod's
// environment variables, matching the syntax of Docker links.
// Optional: Defaults to true.
// +optional
optional bool enableServiceLinks = 30;
}
......
......@@ -2920,6 +2920,7 @@ type PodSpec struct {
RuntimeClassName *string `json:"runtimeClassName,omitempty" protobuf:"bytes,29,opt,name=runtimeClassName"`
// EnableServiceLinks indicates whether information about services should be injected into pod's
// environment variables, matching the syntax of Docker links.
// Optional: Defaults to true.
// +optional
EnableServiceLinks *bool `json:"enableServiceLinks,omitempty" protobuf:"varint,30,opt,name=enableServiceLinks"`
}
......
......@@ -1540,7 +1540,7 @@ var map_PodSpec = map[string]string{
"dnsConfig": "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy.",
"readinessGates": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md",
"runtimeClassName": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://github.com/kubernetes/community/blob/master/keps/sig-node/0014-runtime-class.md This is an alpha feature and may change in the future.",
"enableServiceLinks": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links.",
"enableServiceLinks": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.",
}
func (PodSpec) SwaggerDoc() map[string]string {
......
......@@ -43,6 +43,7 @@ message ClusterRole {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// Rules holds all the PolicyRules for this ClusterRole
// +optional
repeated PolicyRule rules = 2;
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
......@@ -121,6 +122,7 @@ message Role {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// Rules holds all the PolicyRules for this Role
// +optional
repeated PolicyRule rules = 2;
}
......
......@@ -108,6 +108,7 @@ type Role struct {
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Rules holds all the PolicyRules for this Role
// +optional
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
}
......@@ -170,6 +171,7 @@ type ClusterRole struct {
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Rules holds all the PolicyRules for this ClusterRole
// +optional
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
......
......@@ -43,6 +43,7 @@ message ClusterRole {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// Rules holds all the PolicyRules for this ClusterRole
// +optional
repeated PolicyRule rules = 2;
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
......@@ -122,6 +123,7 @@ message Role {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// Rules holds all the PolicyRules for this Role
// +optional
repeated PolicyRule rules = 2;
}
......
......@@ -110,6 +110,7 @@ type Role struct {
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Rules holds all the PolicyRules for this Role
// +optional
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
}
......@@ -172,6 +173,7 @@ type ClusterRole struct {
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Rules holds all the PolicyRules for this ClusterRole
// +optional
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
......
......@@ -43,6 +43,7 @@ message ClusterRole {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// Rules holds all the PolicyRules for this ClusterRole
// +optional
repeated PolicyRule rules = 2;
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
......@@ -122,6 +123,7 @@ message Role {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// Rules holds all the PolicyRules for this Role
// +optional
repeated PolicyRule rules = 2;
}
......
......@@ -109,6 +109,7 @@ type Role struct {
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Rules holds all the PolicyRules for this Role
// +optional
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
}
......@@ -171,6 +172,7 @@ type ClusterRole struct {
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Rules holds all the PolicyRules for this ClusterRole
// +optional
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
// If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be
......
......@@ -56,7 +56,9 @@ func TestNewWithDelegate(t *testing.T) {
w.WriteHeader(http.StatusForbidden)
})
delegatePostStartHookChan := make(chan struct{})
delegateServer.AddPostStartHook("delegate-post-start-hook", func(context PostStartHookContext) error {
defer close(delegatePostStartHookChan)
return nil
})
......@@ -82,7 +84,9 @@ func TestNewWithDelegate(t *testing.T) {
w.WriteHeader(http.StatusUnauthorized)
})
wrappingPostStartHookChan := make(chan struct{})
wrappingServer.AddPostStartHook("wrapping-post-start-hook", func(context PostStartHookContext) error {
defer close(wrappingPostStartHookChan)
return nil
})
......@@ -94,6 +98,10 @@ func TestNewWithDelegate(t *testing.T) {
server := httptest.NewServer(wrappingServer.Handler)
defer server.Close()
// Wait for the hooks to finish before checking the response
<-delegatePostStartHookChan
<-wrappingPostStartHookChan
checkPath(server.URL, http.StatusOK, `{
"paths": [
"/apis",
......
......@@ -35,6 +35,7 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//staging/src/k8s.io/cloud-provider/features:all-srcs",
"//staging/src/k8s.io/cloud-provider/node:all-srcs",
],
tags = ["automanaged"],
......
......@@ -91,6 +91,10 @@
"Rev": "5f041e8faa004a95c88a202771f4cc3e991971e6"
},
{
"ImportPath": "github.com/spf13/pflag",
"Rev": "583c0c0531f06d5278b7d917446061adc344b5cd"
},
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "de0752318171da717af4ce24d0a2e8626afaeb11"
},
......@@ -459,6 +463,10 @@
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/util/feature",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/client-go/discovery",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
......
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["gce.go"],
importmap = "k8s.io/kubernetes/vendor/k8s.io/cloud-provider/features",
importpath = "k8s.io/cloud-provider/features",
visibility = ["//visibility:public"],
deps = ["//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
/*
Copyright 2019 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 features
import (
utilfeature "k8s.io/apiserver/pkg/util/feature"
)
// TODO: this file should ideally live in k8s.io/cloud-provider-gcp, but it is
// temporarily placed here to remove dependencies to k8s.io/kubernetes in the
// in-tree GCE cloud provider. Move this to k8s.io/cloud-provider-gcp as soon
// as it's ready to be used
const (
// owner: @verult
// GA: v1.13
//
// Enables the regional PD feature on GCE.
GCERegionalPersistentDisk utilfeature.Feature = "GCERegionalPersistentDisk"
)
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["taints.go"],
srcs = [
"address.go",
"taints.go",
],
importmap = "k8s.io/kubernetes/vendor/k8s.io/cloud-provider/node/helpers",
importpath = "k8s.io/cloud-provider/node/helpers",
visibility = ["//visibility:public"],
......@@ -31,3 +34,13 @@ filegroup(
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["address_test.go"],
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
],
)
/*
Copyright 2019 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 helpers
import (
"k8s.io/api/core/v1"
)
// AddToNodeAddresses appends the NodeAddresses to the passed-by-pointer slice,
// only if they do not already exist
func AddToNodeAddresses(addresses *[]v1.NodeAddress, addAddresses ...v1.NodeAddress) {
for _, add := range addAddresses {
exists := false
for _, existing := range *addresses {
if existing.Address == add.Address && existing.Type == add.Type {
exists = true
break
}
}
if !exists {
*addresses = append(*addresses, add)
}
}
}
/*
Copyright 2019 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 helpers
import (
"testing"
"k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
)
func TestAddToNodeAddresses(t *testing.T) {
testCases := []struct {
name string
existing []v1.NodeAddress
toAdd []v1.NodeAddress
expected []v1.NodeAddress
}{
{
name: "add no addresses to empty node addresses",
existing: []v1.NodeAddress{},
toAdd: []v1.NodeAddress{},
expected: []v1.NodeAddress{},
},
{
name: "add new node addresses to empty existing addresses",
existing: []v1.NodeAddress{},
toAdd: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeHostName, Address: "localhost"},
},
expected: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeHostName, Address: "localhost"},
},
},
{
name: "add a duplicate node address",
existing: []v1.NodeAddress{},
toAdd: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
},
expected: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
},
},
{
name: "add 1 new and 1 duplicate address",
existing: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
},
toAdd: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeHostName, Address: "localhost"},
},
expected: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeHostName, Address: "localhost"},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
AddToNodeAddresses(&tc.existing, tc.toAdd...)
if !apiequality.Semantic.DeepEqual(tc.expected, tc.existing) {
t.Errorf("expected: %v, got: %v", tc.expected, tc.existing)
}
})
}
}
......@@ -719,7 +719,7 @@ func waitForHistoryCreated(c clientset.Interface, ns string, label map[string]st
listHistoryFn := func() (bool, error) {
selector := labels.Set(label).AsSelector()
options := metav1.ListOptions{LabelSelector: selector.String()}
historyList, err := c.AppsV1beta1().ControllerRevisions(ns).List(options)
historyList, err := c.AppsV1().ControllerRevisions(ns).List(options)
if err != nil {
return false, err
}
......
......@@ -789,7 +789,7 @@ var _ = SIGDescribe("StatefulSet", func() {
ss := framework.NewStatefulSet(ssName, ns, headlessSvcName, 1, nil, nil, labels)
sst := framework.NewStatefulSetTester(c)
sst.SetHttpProbe(ss)
ss, err := c.AppsV1beta1().StatefulSets(ns).Create(ss)
ss, err := c.AppsV1().StatefulSets(ns).Create(ss)
Expect(err).NotTo(HaveOccurred())
sst.WaitForRunningAndReady(*ss.Spec.Replicas, ss)
ss = sst.WaitForStatus(ss)
......@@ -798,7 +798,7 @@ var _ = SIGDescribe("StatefulSet", func() {
scale := framework.NewStatefulSetScale(ss)
scaleResult := &appsv1beta2.Scale{}
err = c.AppsV1beta2().RESTClient().Get().AbsPath("/apis/apps/v1beta2").Namespace(ns).Resource("statefulsets").Name(ssName).SubResource("scale").Do().Into(scale)
err = c.AppsV1().RESTClient().Get().AbsPath("/apis/apps/v1").Namespace(ns).Resource("statefulsets").Name(ssName).SubResource("scale").Do().Into(scale)
if err != nil {
framework.Failf("Failed to get scale subresource: %v", err)
}
......@@ -808,14 +808,14 @@ var _ = SIGDescribe("StatefulSet", func() {
By("updating a scale subresource")
scale.ResourceVersion = "" //unconditionally update to 2 replicas
scale.Spec.Replicas = 2
err = c.AppsV1beta2().RESTClient().Put().AbsPath("/apis/apps/v1beta2").Namespace(ns).Resource("statefulsets").Name(ssName).SubResource("scale").Body(scale).Do().Into(scaleResult)
err = c.AppsV1().RESTClient().Put().AbsPath("/apis/apps/v1").Namespace(ns).Resource("statefulsets").Name(ssName).SubResource("scale").Body(scale).Do().Into(scaleResult)
if err != nil {
framework.Failf("Failed to put scale subresource: %v", err)
}
Expect(scaleResult.Spec.Replicas).To(Equal(int32(2)))
By("verifying the statefulset Spec.Replicas was modified")
ss, err = c.AppsV1beta1().StatefulSets(ns).Get(ssName, metav1.GetOptions{})
ss, err = c.AppsV1().StatefulSets(ns).Get(ssName, metav1.GetOptions{})
if err != nil {
framework.Failf("Failed to get statefulset resource: %v", err)
}
......
......@@ -65,6 +65,7 @@ go_library(
"//pkg/kubelet/sysctl:go_default_library",
"//pkg/kubelet/util/format:go_default_library",
"//pkg/master/ports:go_default_library",
"//pkg/registry/core/service/portallocator:go_default_library",
"//pkg/scheduler/algorithm/predicates:go_default_library",
"//pkg/scheduler/metrics:go_default_library",
"//pkg/scheduler/nodeinfo:go_default_library",
......
......@@ -40,6 +40,7 @@ import (
"k8s.io/client-go/util/retry"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/registry/core/service/portallocator"
testutils "k8s.io/kubernetes/test/utils"
imageutils "k8s.io/kubernetes/test/utils/image"
......@@ -557,7 +558,7 @@ func (j *ServiceTestJig) ChangeServiceNodePortOrFail(namespace, name string, ini
service, err = j.UpdateService(namespace, name, func(s *v1.Service) {
s.Spec.Ports[0].NodePort = int32(newPort)
})
if err != nil && strings.Contains(err.Error(), "provided port is already allocated") {
if err != nil && strings.Contains(err.Error(), portallocator.ErrAllocated.Error()) {
Logf("tried nodePort %d, but it is in use, will try another", newPort)
continue
}
......
......@@ -171,7 +171,7 @@ var _ = framework.KubeDescribe("LocalStorageEviction [Slow] [Serial] [Disruptive
expectedStarvedResource := v1.ResourceEphemeralStorage
Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() {
tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) {
diskConsumed := resource.MustParse("100Mi")
diskConsumed := resource.MustParse("200Mi")
summary := eventuallyGetSummary()
availableBytes := *(summary.Node.Fs.AvailableBytes)
initialConfig.EvictionHard = map[string]string{string(evictionapi.SignalNodeFsAvailable): fmt.Sprintf("%d", availableBytes-uint64(diskConsumed.Value()))}
......@@ -200,7 +200,7 @@ var _ = framework.KubeDescribe("LocalStorageSoftEviction [Slow] [Serial] [Disrup
expectedStarvedResource := v1.ResourceEphemeralStorage
Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() {
tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) {
diskConsumed := resource.MustParse("100Mi")
diskConsumed := resource.MustParse("200Mi")
summary := eventuallyGetSummary()
availableBytes := *(summary.Node.Fs.AvailableBytes)
if availableBytes <= uint64(diskConsumed.Value()) {
......@@ -459,10 +459,11 @@ func runEvictionTest(f *framework.Framework, pressureTimeout time.Duration, expe
// Sleep so that pods requesting local storage do not fail to schedule
time.Sleep(30 * time.Second)
By("seting up pods to be used by tests")
pods := []*v1.Pod{}
for _, spec := range testSpecs {
By(fmt.Sprintf("creating pod with container: %s", spec.pod.Name))
f.PodClient().CreateSync(spec.pod)
pods = append(pods, spec.pod)
}
f.PodClient().CreateBatch(pods)
})
It("should eventually evict all of the correct pods", func() {
......@@ -831,7 +832,7 @@ func diskConsumingPod(name string, diskConsumedMB int, volumeSource *v1.VolumeSo
path = volumeMountPath
}
// Each iteration writes 1 Mb, so do diskConsumedMB iterations.
return podWithCommand(volumeSource, resources, diskConsumedMB, name, fmt.Sprintf("dd if=/dev/urandom of=%s${i} bs=1048576 count=1 2>/dev/null;", filepath.Join(path, "file")))
return podWithCommand(volumeSource, resources, diskConsumedMB, name, fmt.Sprintf("dd if=/dev/urandom of=%s${i} bs=1048576 count=1 2>/dev/null; sleep .1;", filepath.Join(path, "file")))
}
func pidConsumingPod(name string, numProcesses int) *v1.Pod {
......
......@@ -29,7 +29,6 @@ go_test(
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/master:go_default_library",
"//staging/src/k8s.io/api/apps/v1:go_default_library",
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/api/networking/v1:go_default_library",
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
......
......@@ -25,7 +25,7 @@ import (
"testing"
"time"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
......@@ -49,20 +49,22 @@ func TestRun(t *testing.T) {
// test whether the server is really healthy after /healthz told us so
t.Logf("Creating Deployment directly after being healthy")
var replicas int32 = 1
_, err = client.AppsV1beta1().Deployments("default").Create(&appsv1beta1.Deployment{
_, err = client.AppsV1().Deployments("default").Create(&appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1beta1",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "test",
Labels: map[string]string{"foo": "bar"},
},
Spec: appsv1beta1.DeploymentSpec{
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Strategy: appsv1beta1.DeploymentStrategy{
Type: appsv1beta1.RollingUpdateDeploymentStrategyType,
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar"},
......
......@@ -12,7 +12,7 @@ go_test(
tags = ["integration"],
deps = [
"//cmd/kube-apiserver/app/testing:go_default_library",
"//staging/src/k8s.io/api/apps/v1beta2:go_default_library",
"//staging/src/k8s.io/api/apps/v1:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
......
......@@ -25,7 +25,7 @@ import (
_ "github.com/coreos/etcd/etcdserver/api/v3rpc" // Force package logger init.
"github.com/coreos/pkg/capnslog"
appsv1beta2 "k8s.io/api/apps/v1beta2"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
......@@ -141,13 +141,13 @@ func TestScaleSubresources(t *testing.T) {
if _, err := clientSet.CoreV1().ReplicationControllers("default").Create(rcStub); err != nil {
t.Fatal(err)
}
if _, err := clientSet.AppsV1beta2().ReplicaSets("default").Create(rsStub); err != nil {
if _, err := clientSet.AppsV1().ReplicaSets("default").Create(rsStub); err != nil {
t.Fatal(err)
}
if _, err := clientSet.AppsV1beta2().Deployments("default").Create(deploymentStub); err != nil {
if _, err := clientSet.AppsV1().Deployments("default").Create(deploymentStub); err != nil {
t.Fatal(err)
}
if _, err := clientSet.AppsV1beta2().StatefulSets("default").Create(ssStub); err != nil {
if _, err := clientSet.AppsV1().StatefulSets("default").Create(ssStub); err != nil {
t.Fatal(err)
}
......@@ -203,19 +203,19 @@ var (
Spec: corev1.ReplicationControllerSpec{Selector: podStub.Labels, Replicas: &replicas, Template: &podStub},
}
rsStub = &appsv1beta2.ReplicaSet{
rsStub = &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{Name: "test"},
Spec: appsv1beta2.ReplicaSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
Spec: appsv1.ReplicaSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
}
deploymentStub = &appsv1beta2.Deployment{
deploymentStub = &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{Name: "test"},
Spec: appsv1beta2.DeploymentSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
Spec: appsv1.DeploymentSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
}
ssStub = &appsv1beta2.StatefulSet{
ssStub = &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{Name: "test"},
Spec: appsv1beta2.StatefulSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
Spec: appsv1.StatefulSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
}
)
......
......@@ -787,7 +787,6 @@ k8s.io/kubernetes/pkg/util/config,jszczepkowski,1,
k8s.io/kubernetes/pkg/util/configz,ixdy,1,
k8s.io/kubernetes/pkg/util/dbus,roberthbailey,1,
k8s.io/kubernetes/pkg/util/env,asalkeld,0,
k8s.io/kubernetes/pkg/util/exec,krousey,1,
k8s.io/kubernetes/pkg/util/goroutinemap,saad-ali,0,
k8s.io/kubernetes/pkg/util/hash,timothysc,1,
k8s.io/kubernetes/pkg/util/i18n,brendandburns,0,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment