vendor: bump to Kube 1.9/master

Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
Antonio Murdaca 2017-11-13 11:33:25 +01:00
parent 7076c73172
commit 7a675ccd92
No known key found for this signature in database
GPG key ID: B2BEAD150DE936B9
202 changed files with 8543 additions and 7270 deletions

View file

@ -45,12 +45,6 @@ const (
// to one container of a pod.
SeccompContainerAnnotationKeyPrefix string = "container.seccomp.security.alpha.kubernetes.io/"
// CreatedByAnnotation represents the key used to store the spec(json)
// used to create the resource.
// This field is deprecated in favor of ControllerRef (see #44407).
// TODO(#50720): Remove this field in v1.9.
CreatedByAnnotation = "kubernetes.io/created-by"
// PreferAvoidPodsAnnotationKey represents the key of preferAvoidPods data (json serialized)
// in the Annotations of a Node.
PreferAvoidPodsAnnotationKey string = "scheduler.alpha.kubernetes.io/preferAvoidPods"

View file

@ -265,6 +265,11 @@ func IsIntegerResourceName(str string) bool {
return integerResources.Has(str) || IsExtendedResourceName(api.ResourceName(str))
}
// Extended and HugePages resources
func IsScalarResourceName(name api.ResourceName) bool {
return IsExtendedResourceName(name) || IsHugePageResourceName(name)
}
// this function aims to check if the service's ClusterIP is set or not
// the objective is not to perform validation here
func IsServiceIPSet(service *api.Service) bool {

View file

@ -24,11 +24,12 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/v1"
)
func init() {
Install(api.GroupFactoryRegistry, api.Registry, api.Scheme)
Install(legacyscheme.GroupFactoryRegistry, legacyscheme.Registry, legacyscheme.Scheme)
}
// Install registers the API group and adds types to a scheme

View file

@ -0,0 +1,46 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package legacyscheme
import (
"os"
"k8s.io/apimachinery/pkg/apimachinery/announced"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
)
// GroupFactoryRegistry is the APIGroupFactoryRegistry (overlaps a bit with Registry, see comments in package for details)
var GroupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
// Registry is an instance of an API registry. This is an interim step to start removing the idea of a global
// API registry.
var Registry = registered.NewOrDie(os.Getenv("KUBE_API_VERSIONS"))
// Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered.
// NOTE: If you are copying this file to start a new api group, STOP! Copy the
// extensions group instead. This Scheme is special and should appear ONLY in
// the api group, unless you really know what you're doing.
// TODO(lavalamp): make the above error impossible.
var Scheme = runtime.NewScheme()
// Codecs provides access to encoding and decoding for the scheme
var Codecs = serializer.NewCodecFactory(Scheme)
// ParameterCodec handles versioning of objects that are converted to query parameters.
var ParameterCodec = runtime.NewParameterCodec(Scheme)

View file

@ -17,42 +17,17 @@ limitations under the License.
package api
import (
"os"
"k8s.io/apimachinery/pkg/apimachinery/announced"
"k8s.io/apimachinery/pkg/apimachinery/registered"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
)
// GroupFactoryRegistry is the APIGroupFactoryRegistry (overlaps a bit with Registry, see comments in package for details)
var GroupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
// Registry is an instance of an API registry. This is an interim step to start removing the idea of a global
// API registry.
var Registry = registered.NewOrDie(os.Getenv("KUBE_API_VERSIONS"))
// Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered.
// NOTE: If you are copying this file to start a new api group, STOP! Copy the
// extensions group instead. This Scheme is special and should appear ONLY in
// the api group, unless you really know what you're doing.
// TODO(lavalamp): make the above error impossible.
var Scheme = runtime.NewScheme()
// Codecs provides access to encoding and decoding for the scheme
var Codecs = serializer.NewCodecFactory(Scheme)
// GroupName is the group name use in this package
const GroupName = ""
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// ParameterCodec handles versioning of objects that are converted to query parameters.
var ParameterCodec = runtime.NewParameterCodec(Scheme)
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()

View file

@ -343,7 +343,7 @@ type PersistentVolumeSource struct {
NFS *NFSVolumeSource
// RBD represents a Rados Block Device mount on the host that shares a pod's lifetime
// +optional
RBD *RBDVolumeSource
RBD *RBDPersistentVolumeSource
// Quobyte represents a Quobyte mount on the host that shares a pod's lifetime
// +optional
Quobyte *QuobyteVolumeSource
@ -383,7 +383,7 @@ type PersistentVolumeSource struct {
PortworxVolume *PortworxVolumeSource
// ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
// +optional
ScaleIO *ScaleIOVolumeSource
ScaleIO *ScaleIOPersistentVolumeSource
// Local represents directly-attached storage with node affinity
// +optional
Local *LocalVolumeSource
@ -1006,6 +1006,37 @@ type RBDVolumeSource struct {
ReadOnly bool
}
// Represents a Rados Block Device mount that lasts the lifetime of a pod.
// RBD volumes support ownership management and SELinux relabeling.
type RBDPersistentVolumeSource struct {
// Required: CephMonitors is a collection of Ceph monitors
CephMonitors []string
// Required: RBDImage is the rados image name
RBDImage string
// Filesystem type to mount.
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
// TODO: how do we prevent errors in the filesystem from compromising the machine
// +optional
FSType string
// Optional: RadosPool is the rados pool name,default is rbd
// +optional
RBDPool string
// Optional: RBDUser is the rados user name, default is admin
// +optional
RadosUser string
// Optional: Keyring is the path to key ring for RBDUser, default is /etc/ceph/keyring
// +optional
Keyring string
// Optional: SecretRef is reference to the authentication secret for User, default is empty.
// +optional
SecretRef *SecretReference
// Optional: Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
// +optional
ReadOnly bool
}
// Represents a cinder volume resource in Openstack. A Cinder volume
// must exist before mounting to a container. The volume must also be
// in the same region as the kubelet. Cinder volumes support ownership
@ -1238,7 +1269,7 @@ type AzureDiskVolumeSource struct {
// the ReadOnly setting in VolumeMounts.
// +optional
ReadOnly *bool
// Expected values Shared: mulitple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared
// Expected values Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared
Kind *AzureDataDiskKind
}
@ -1254,13 +1285,13 @@ type ScaleIOVolumeSource struct {
// Flag to enable/disable SSL communication with Gateway, default false
// +optional
SSLEnabled bool
// The name of the Protection Domain for the configured storage (defaults to "default").
// The name of the ScaleIO Protection Domain for the configured storage.
// +optional
ProtectionDomain string
// The Storage Pool associated with the protection domain (defaults to "default").
// The ScaleIO Storage Pool associated with the protection domain.
// +optional
StoragePool string
// Indicates whether the storage for a volume should be thick or thin (defaults to "thin").
// Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned.
// +optional
StorageMode string
// The name of a volume already created in the ScaleIO system
@ -1277,6 +1308,42 @@ type ScaleIOVolumeSource struct {
ReadOnly bool
}
// ScaleIOPersistentVolumeSource represents a persistent ScaleIO volume that can be defined
// by a an admin via a storage class, for instance.
type ScaleIOPersistentVolumeSource struct {
// The host address of the ScaleIO API Gateway.
Gateway string
// The name of the storage system as configured in ScaleIO.
System string
// SecretRef references to the secret for ScaleIO user and other
// sensitive information. If this is not provided, Login operation will fail.
SecretRef *SecretReference
// Flag to enable/disable SSL communication with Gateway, default false
// +optional
SSLEnabled bool
// The name of the ScaleIO Protection Domain for the configured storage.
// +optional
ProtectionDomain string
// The ScaleIO Storage Pool associated with the protection domain.
// +optional
StoragePool string
// Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned.
// +optional
StorageMode string
// The name of a volume created in the ScaleIO system
// that is associated with this volume source.
VolumeName string
// Filesystem type to mount.
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
// +optional
FSType string
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
// +optional
ReadOnly bool
}
// Represents a StorageOS persistent volume resource.
type StorageOSVolumeSource struct {
// VolumeName is the human-readable name of the StorageOS volume. Volume
@ -2229,7 +2296,7 @@ type Taint struct {
// TimeAdded represents the time at which the taint was added.
// It is only written for NoExecute taints.
// +optional
TimeAdded metav1.Time
TimeAdded *metav1.Time
}
type TaintEffect string
@ -3117,7 +3184,7 @@ type NodeConfigSource struct {
type DaemonEndpoint struct {
/*
The port tag was not properly in quotes in earlier releases, so it must be
uppercased for backwards compat (since it was falling back to var name of
uppercase for backwards compatibility (since it was falling back to var name of
'Port').
*/
@ -4043,7 +4110,7 @@ const (
// BasicAuthPasswordKey is the key of the password or token for SecretTypeBasicAuth secrets
BasicAuthPasswordKey = "password"
// SecretTypeSSHAuth contains data needed for SSH authetication.
// SecretTypeSSHAuth contains data needed for SSH authentication.
//
// Required field:
// - Secret.Data["ssh-privatekey"] - private SSH key needed for authentication

View file

@ -1,48 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// TODO: This GetVersion/GetGroup arrangement is temporary and will be replaced
// with a GroupAndVersion type.
package util
import "strings"
func GetVersion(groupVersion string) string {
s := strings.Split(groupVersion, "/")
if len(s) != 2 {
// e.g. return "v1" for groupVersion="v1"
return s[len(s)-1]
}
return s[1]
}
func GetGroup(groupVersion string) string {
s := strings.Split(groupVersion, "/")
if len(s) == 1 {
// e.g. return "" for groupVersion="v1"
return ""
}
return s[0]
}
// GetGroupVersion returns the "group/version". It returns "version" is if group
// is empty. It returns "group/" if version is empty.
func GetGroupVersion(group, version string) string {
if len(group) == 0 {
return version
}
return group + "/" + version
}

View file

@ -235,7 +235,9 @@ func Convert_v1_ReplicationController_to_extensions_ReplicaSet(in *v1.Replicatio
func Convert_v1_ReplicationControllerSpec_to_extensions_ReplicaSetSpec(in *v1.ReplicationControllerSpec, out *extensions.ReplicaSetSpec, s conversion.Scope) error {
out.Replicas = *in.Replicas
out.MinReadySeconds = in.MinReadySeconds
if in.Selector != nil {
out.Selector = new(metav1.LabelSelector)
metav1.Convert_map_to_unversioned_LabelSelector(&in.Selector, out.Selector, s)
}
if in.Template != nil {
@ -252,6 +254,15 @@ func Convert_v1_ReplicationControllerStatus_to_extensions_ReplicaSetStatus(in *v
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
for _, cond := range in.Conditions {
out.Conditions = append(out.Conditions, extensions.ReplicaSetCondition{
Type: extensions.ReplicaSetConditionType(cond.Type),
Status: api.ConditionStatus(cond.Status),
LastTransitionTime: cond.LastTransitionTime,
Reason: cond.Reason,
Message: cond.Message,
})
}
return nil
}
@ -294,6 +305,15 @@ func Convert_extensions_ReplicaSetStatus_to_v1_ReplicationControllerStatus(in *e
out.ReadyReplicas = in.ReadyReplicas
out.AvailableReplicas = in.AvailableReplicas
out.ObservedGeneration = in.ObservedGeneration
for _, cond := range in.Conditions {
out.Conditions = append(out.Conditions, v1.ReplicationControllerCondition{
Type: v1.ReplicationControllerConditionType(cond.Type),
Status: v1.ConditionStatus(cond.Status),
LastTransitionTime: cond.LastTransitionTime,
Reason: cond.Reason,
Message: cond.Message,
})
}
return nil
}

View file

@ -368,13 +368,28 @@ func SetDefaults_RBDVolumeSource(obj *v1.RBDVolumeSource) {
}
}
func SetDefaults_RBDPersistentVolumeSource(obj *v1.RBDPersistentVolumeSource) {
if obj.RBDPool == "" {
obj.RBDPool = "rbd"
}
if obj.RadosUser == "" {
obj.RadosUser = "admin"
}
if obj.Keyring == "" {
obj.Keyring = "/etc/ceph/keyring"
}
}
func SetDefaults_ScaleIOVolumeSource(obj *v1.ScaleIOVolumeSource) {
if obj.ProtectionDomain == "" {
obj.ProtectionDomain = "default"
if obj.StorageMode == "" {
obj.StorageMode = "ThinProvisioned"
}
if obj.StoragePool == "" {
obj.StoragePool = "default"
if obj.FSType == "" {
obj.FSType = "xfs"
}
}
func SetDefaults_ScaleIOPersistentVolumeSource(obj *v1.ScaleIOPersistentVolumeSource) {
if obj.StorageMode == "" {
obj.StorageMode = "ThinProvisioned"
}

View file

@ -1,65 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
"fmt"
"k8s.io/api/core/v1"
utilrand "k8s.io/apimachinery/pkg/util/rand"
)
// NameGenerator generates names for objects. Some backends may have more information
// available to guide selection of new names and this interface hides those details.
type NameGenerator interface {
// GenerateName generates a valid name from the base name, adding a random suffix to the
// the base. If base is valid, the returned name must also be valid. The generator is
// responsible for knowing the maximum valid name length.
GenerateName(base string) string
}
// GenerateName will resolve the object name of the provided v1.ObjectMeta to a generated version if
// necessary. It expects that validation for v1.ObjectMeta has already completed (that Base is a
// valid name) and that the NameGenerator generates a name that is also valid.
func GenerateName(u NameGenerator, meta *v1.ObjectMeta) {
if len(meta.GenerateName) == 0 || len(meta.Name) != 0 {
return
}
meta.Name = u.GenerateName(meta.GenerateName)
}
// simpleNameGenerator generates random names.
type simpleNameGenerator struct{}
// SimpleNameGenerator is a generator that returns the name plus a random suffix of five alphanumerics
// when a name is requested. The string is guaranteed to not exceed the length of a standard Kubernetes
// name (63 characters)
var SimpleNameGenerator NameGenerator = simpleNameGenerator{}
const (
// TODO: make this flexible for non-core resources with alternate naming rules.
maxNameLength = 63
randomLength = 5
maxGeneratedNameLength = maxNameLength - randomLength
)
func (simpleNameGenerator) GenerateName(base string) string {
if len(base) > maxGeneratedNameLength {
base = base[:maxGeneratedNameLength]
}
return fmt.Sprintf("%s%s", base, utilrand.String(randomLength))
}

View file

@ -88,12 +88,18 @@ func OpaqueIntResourceName(name string) v1.ResourceName {
var overcommitBlacklist = sets.NewString(string(v1.ResourceNvidiaGPU))
// IsOvercommitAllowed returns true if the resource is in the default
// namespace and not blacklisted.
// namespace and not blacklisted and is not hugepages.
func IsOvercommitAllowed(name v1.ResourceName) bool {
return IsDefaultNamespaceResource(name) &&
!IsHugePageResourceName(name) &&
!overcommitBlacklist.Has(string(name))
}
// Extended and Hugepages resources
func IsScalarResourceName(name v1.ResourceName) bool {
return IsExtendedResourceName(name) || IsHugePageResourceName(name)
}
// this function aims to check if the service's ClusterIP is set or not
// the objective is not to perform validation here
func IsServiceIPSet(service *v1.Service) bool {

View file

@ -305,6 +305,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
Convert_api_ProjectedVolumeSource_To_v1_ProjectedVolumeSource,
Convert_v1_QuobyteVolumeSource_To_api_QuobyteVolumeSource,
Convert_api_QuobyteVolumeSource_To_v1_QuobyteVolumeSource,
Convert_v1_RBDPersistentVolumeSource_To_api_RBDPersistentVolumeSource,
Convert_api_RBDPersistentVolumeSource_To_v1_RBDPersistentVolumeSource,
Convert_v1_RBDVolumeSource_To_api_RBDVolumeSource,
Convert_api_RBDVolumeSource_To_v1_RBDVolumeSource,
Convert_v1_RangeAllocation_To_api_RangeAllocation,
@ -333,6 +335,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
Convert_api_ResourceRequirements_To_v1_ResourceRequirements,
Convert_v1_SELinuxOptions_To_api_SELinuxOptions,
Convert_api_SELinuxOptions_To_v1_SELinuxOptions,
Convert_v1_ScaleIOPersistentVolumeSource_To_api_ScaleIOPersistentVolumeSource,
Convert_api_ScaleIOPersistentVolumeSource_To_v1_ScaleIOPersistentVolumeSource,
Convert_v1_ScaleIOVolumeSource_To_api_ScaleIOVolumeSource,
Convert_api_ScaleIOVolumeSource_To_v1_ScaleIOVolumeSource,
Convert_v1_Secret_To_api_Secret,
@ -3124,7 +3128,7 @@ func autoConvert_v1_PersistentVolumeSource_To_api_PersistentVolumeSource(in *v1.
out.HostPath = (*api.HostPathVolumeSource)(unsafe.Pointer(in.HostPath))
out.Glusterfs = (*api.GlusterfsVolumeSource)(unsafe.Pointer(in.Glusterfs))
out.NFS = (*api.NFSVolumeSource)(unsafe.Pointer(in.NFS))
out.RBD = (*api.RBDVolumeSource)(unsafe.Pointer(in.RBD))
out.RBD = (*api.RBDPersistentVolumeSource)(unsafe.Pointer(in.RBD))
out.ISCSI = (*api.ISCSIVolumeSource)(unsafe.Pointer(in.ISCSI))
out.Cinder = (*api.CinderVolumeSource)(unsafe.Pointer(in.Cinder))
out.CephFS = (*api.CephFSPersistentVolumeSource)(unsafe.Pointer(in.CephFS))
@ -3137,7 +3141,7 @@ func autoConvert_v1_PersistentVolumeSource_To_api_PersistentVolumeSource(in *v1.
out.AzureDisk = (*api.AzureDiskVolumeSource)(unsafe.Pointer(in.AzureDisk))
out.PhotonPersistentDisk = (*api.PhotonPersistentDiskVolumeSource)(unsafe.Pointer(in.PhotonPersistentDisk))
out.PortworxVolume = (*api.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
out.ScaleIO = (*api.ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
out.ScaleIO = (*api.ScaleIOPersistentVolumeSource)(unsafe.Pointer(in.ScaleIO))
out.Local = (*api.LocalVolumeSource)(unsafe.Pointer(in.Local))
out.StorageOS = (*api.StorageOSPersistentVolumeSource)(unsafe.Pointer(in.StorageOS))
return nil
@ -3154,7 +3158,7 @@ func autoConvert_api_PersistentVolumeSource_To_v1_PersistentVolumeSource(in *api
out.HostPath = (*v1.HostPathVolumeSource)(unsafe.Pointer(in.HostPath))
out.Glusterfs = (*v1.GlusterfsVolumeSource)(unsafe.Pointer(in.Glusterfs))
out.NFS = (*v1.NFSVolumeSource)(unsafe.Pointer(in.NFS))
out.RBD = (*v1.RBDVolumeSource)(unsafe.Pointer(in.RBD))
out.RBD = (*v1.RBDPersistentVolumeSource)(unsafe.Pointer(in.RBD))
out.Quobyte = (*v1.QuobyteVolumeSource)(unsafe.Pointer(in.Quobyte))
out.ISCSI = (*v1.ISCSIVolumeSource)(unsafe.Pointer(in.ISCSI))
out.FlexVolume = (*v1.FlexVolumeSource)(unsafe.Pointer(in.FlexVolume))
@ -3167,7 +3171,7 @@ func autoConvert_api_PersistentVolumeSource_To_v1_PersistentVolumeSource(in *api
out.AzureDisk = (*v1.AzureDiskVolumeSource)(unsafe.Pointer(in.AzureDisk))
out.PhotonPersistentDisk = (*v1.PhotonPersistentDiskVolumeSource)(unsafe.Pointer(in.PhotonPersistentDisk))
out.PortworxVolume = (*v1.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
out.ScaleIO = (*v1.ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
out.ScaleIO = (*v1.ScaleIOPersistentVolumeSource)(unsafe.Pointer(in.ScaleIO))
out.Local = (*v1.LocalVolumeSource)(unsafe.Pointer(in.Local))
out.StorageOS = (*v1.StorageOSPersistentVolumeSource)(unsafe.Pointer(in.StorageOS))
return nil
@ -4062,6 +4066,40 @@ func Convert_api_QuobyteVolumeSource_To_v1_QuobyteVolumeSource(in *api.QuobyteVo
return autoConvert_api_QuobyteVolumeSource_To_v1_QuobyteVolumeSource(in, out, s)
}
func autoConvert_v1_RBDPersistentVolumeSource_To_api_RBDPersistentVolumeSource(in *v1.RBDPersistentVolumeSource, out *api.RBDPersistentVolumeSource, s conversion.Scope) error {
out.CephMonitors = *(*[]string)(unsafe.Pointer(&in.CephMonitors))
out.RBDImage = in.RBDImage
out.FSType = in.FSType
out.RBDPool = in.RBDPool
out.RadosUser = in.RadosUser
out.Keyring = in.Keyring
out.SecretRef = (*api.SecretReference)(unsafe.Pointer(in.SecretRef))
out.ReadOnly = in.ReadOnly
return nil
}
// Convert_v1_RBDPersistentVolumeSource_To_api_RBDPersistentVolumeSource is an autogenerated conversion function.
func Convert_v1_RBDPersistentVolumeSource_To_api_RBDPersistentVolumeSource(in *v1.RBDPersistentVolumeSource, out *api.RBDPersistentVolumeSource, s conversion.Scope) error {
return autoConvert_v1_RBDPersistentVolumeSource_To_api_RBDPersistentVolumeSource(in, out, s)
}
func autoConvert_api_RBDPersistentVolumeSource_To_v1_RBDPersistentVolumeSource(in *api.RBDPersistentVolumeSource, out *v1.RBDPersistentVolumeSource, s conversion.Scope) error {
out.CephMonitors = *(*[]string)(unsafe.Pointer(&in.CephMonitors))
out.RBDImage = in.RBDImage
out.FSType = in.FSType
out.RBDPool = in.RBDPool
out.RadosUser = in.RadosUser
out.Keyring = in.Keyring
out.SecretRef = (*v1.SecretReference)(unsafe.Pointer(in.SecretRef))
out.ReadOnly = in.ReadOnly
return nil
}
// Convert_api_RBDPersistentVolumeSource_To_v1_RBDPersistentVolumeSource is an autogenerated conversion function.
func Convert_api_RBDPersistentVolumeSource_To_v1_RBDPersistentVolumeSource(in *api.RBDPersistentVolumeSource, out *v1.RBDPersistentVolumeSource, s conversion.Scope) error {
return autoConvert_api_RBDPersistentVolumeSource_To_v1_RBDPersistentVolumeSource(in, out, s)
}
func autoConvert_v1_RBDVolumeSource_To_api_RBDVolumeSource(in *v1.RBDVolumeSource, out *api.RBDVolumeSource, s conversion.Scope) error {
out.CephMonitors = *(*[]string)(unsafe.Pointer(&in.CephMonitors))
out.RBDImage = in.RBDImage
@ -4458,6 +4496,44 @@ func Convert_api_SELinuxOptions_To_v1_SELinuxOptions(in *api.SELinuxOptions, out
return autoConvert_api_SELinuxOptions_To_v1_SELinuxOptions(in, out, s)
}
func autoConvert_v1_ScaleIOPersistentVolumeSource_To_api_ScaleIOPersistentVolumeSource(in *v1.ScaleIOPersistentVolumeSource, out *api.ScaleIOPersistentVolumeSource, s conversion.Scope) error {
out.Gateway = in.Gateway
out.System = in.System
out.SecretRef = (*api.SecretReference)(unsafe.Pointer(in.SecretRef))
out.SSLEnabled = in.SSLEnabled
out.ProtectionDomain = in.ProtectionDomain
out.StoragePool = in.StoragePool
out.StorageMode = in.StorageMode
out.VolumeName = in.VolumeName
out.FSType = in.FSType
out.ReadOnly = in.ReadOnly
return nil
}
// Convert_v1_ScaleIOPersistentVolumeSource_To_api_ScaleIOPersistentVolumeSource is an autogenerated conversion function.
func Convert_v1_ScaleIOPersistentVolumeSource_To_api_ScaleIOPersistentVolumeSource(in *v1.ScaleIOPersistentVolumeSource, out *api.ScaleIOPersistentVolumeSource, s conversion.Scope) error {
return autoConvert_v1_ScaleIOPersistentVolumeSource_To_api_ScaleIOPersistentVolumeSource(in, out, s)
}
func autoConvert_api_ScaleIOPersistentVolumeSource_To_v1_ScaleIOPersistentVolumeSource(in *api.ScaleIOPersistentVolumeSource, out *v1.ScaleIOPersistentVolumeSource, s conversion.Scope) error {
out.Gateway = in.Gateway
out.System = in.System
out.SecretRef = (*v1.SecretReference)(unsafe.Pointer(in.SecretRef))
out.SSLEnabled = in.SSLEnabled
out.ProtectionDomain = in.ProtectionDomain
out.StoragePool = in.StoragePool
out.StorageMode = in.StorageMode
out.VolumeName = in.VolumeName
out.FSType = in.FSType
out.ReadOnly = in.ReadOnly
return nil
}
// Convert_api_ScaleIOPersistentVolumeSource_To_v1_ScaleIOPersistentVolumeSource is an autogenerated conversion function.
func Convert_api_ScaleIOPersistentVolumeSource_To_v1_ScaleIOPersistentVolumeSource(in *api.ScaleIOPersistentVolumeSource, out *v1.ScaleIOPersistentVolumeSource, s conversion.Scope) error {
return autoConvert_api_ScaleIOPersistentVolumeSource_To_v1_ScaleIOPersistentVolumeSource(in, out, s)
}
func autoConvert_v1_ScaleIOVolumeSource_To_api_ScaleIOVolumeSource(in *v1.ScaleIOVolumeSource, out *api.ScaleIOVolumeSource, s conversion.Scope) error {
out.Gateway = in.Gateway
out.System = in.System
@ -5101,7 +5177,7 @@ func autoConvert_v1_Taint_To_api_Taint(in *v1.Taint, out *api.Taint, s conversio
out.Key = in.Key
out.Value = in.Value
out.Effect = api.TaintEffect(in.Effect)
out.TimeAdded = in.TimeAdded
out.TimeAdded = (*meta_v1.Time)(unsafe.Pointer(in.TimeAdded))
return nil
}
@ -5114,7 +5190,7 @@ func autoConvert_api_Taint_To_v1_Taint(in *api.Taint, out *v1.Taint, s conversio
out.Key = in.Key
out.Value = in.Value
out.Effect = v1.TaintEffect(in.Effect)
out.TimeAdded = in.TimeAdded
out.TimeAdded = (*meta_v1.Time)(unsafe.Pointer(in.TimeAdded))
return nil
}

View file

@ -137,7 +137,7 @@ func SetObjectDefaults_PersistentVolume(in *v1.PersistentVolume) {
SetDefaults_HostPathVolumeSource(in.Spec.PersistentVolumeSource.HostPath)
}
if in.Spec.PersistentVolumeSource.RBD != nil {
SetDefaults_RBDVolumeSource(in.Spec.PersistentVolumeSource.RBD)
SetDefaults_RBDPersistentVolumeSource(in.Spec.PersistentVolumeSource.RBD)
}
if in.Spec.PersistentVolumeSource.ISCSI != nil {
SetDefaults_ISCSIVolumeSource(in.Spec.PersistentVolumeSource.ISCSI)
@ -146,7 +146,7 @@ func SetObjectDefaults_PersistentVolume(in *v1.PersistentVolume) {
SetDefaults_AzureDiskVolumeSource(in.Spec.PersistentVolumeSource.AzureDisk)
}
if in.Spec.PersistentVolumeSource.ScaleIO != nil {
SetDefaults_ScaleIOVolumeSource(in.Spec.PersistentVolumeSource.ScaleIO)
SetDefaults_ScaleIOPersistentVolumeSource(in.Spec.PersistentVolumeSource.ScaleIO)
}
}

View file

@ -25,7 +25,7 @@ import (
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/api"
apiutil "k8s.io/kubernetes/pkg/api/util"
"k8s.io/kubernetes/pkg/api/legacyscheme"
)
// ValidateEvent makes sure that the event makes sense.
@ -63,12 +63,16 @@ func ValidateEvent(event *api.Event) field.ErrorList {
// Check whether the kind in groupVersion is scoped at the root of the api hierarchy
func isNamespacedKind(kind, groupVersion string) (bool, error) {
group := apiutil.GetGroup(groupVersion)
g, err := api.Registry.Group(group)
gv, err := schema.ParseGroupVersion(groupVersion)
if err != nil {
return false, err
}
restMapping, err := g.RESTMapper.RESTMapping(schema.GroupKind{Group: group, Kind: kind}, apiutil.GetVersion(groupVersion))
g, err := legacyscheme.Registry.Group(gv.Group)
if err != nil {
return false, err
}
restMapping, err := g.RESTMapper.RESTMapping(schema.GroupKind{Group: gv.Group, Kind: kind}, gv.Version)
if err != nil {
return false, err
}

View file

@ -45,6 +45,7 @@ import (
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/helper"
"k8s.io/kubernetes/pkg/api/legacyscheme"
apiservice "k8s.io/kubernetes/pkg/api/service"
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
@ -1087,6 +1088,17 @@ func validateRBDVolumeSource(rbd *api.RBDVolumeSource, fldPath *field.Path) fiel
return allErrs
}
func validateRBDPersistentVolumeSource(rbd *api.RBDPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(rbd.CephMonitors) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("monitors"), ""))
}
if len(rbd.RBDImage) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("image"), ""))
}
return allErrs
}
func validateCinderVolumeSource(cd *api.CinderVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(cd.VolumeID) == 0 {
@ -1233,6 +1245,20 @@ func validateScaleIOVolumeSource(sio *api.ScaleIOVolumeSource, fldPath *field.Pa
return allErrs
}
func validateScaleIOPersistentVolumeSource(sio *api.ScaleIOPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if sio.Gateway == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("gateway"), ""))
}
if sio.System == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("system"), ""))
}
if sio.VolumeName == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
}
return allErrs
}
func validateLocalVolumeSource(ls *api.LocalVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if ls.Path == "" {
@ -1379,7 +1405,7 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
allErrs = append(allErrs, field.Forbidden(specPath.Child("rbd"), "may not specify more than 1 volume type"))
} else {
numVolumes++
allErrs = append(allErrs, validateRBDVolumeSource(pv.Spec.RBD, specPath.Child("rbd"))...)
allErrs = append(allErrs, validateRBDPersistentVolumeSource(pv.Spec.RBD, specPath.Child("rbd"))...)
}
}
if pv.Spec.Quobyte != nil {
@ -1477,7 +1503,7 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
allErrs = append(allErrs, field.Forbidden(specPath.Child("scaleIO"), "may not specify more than 1 volume type"))
} else {
numVolumes++
allErrs = append(allErrs, validateScaleIOVolumeSource(pv.Spec.ScaleIO, specPath.Child("scaleIO"))...)
allErrs = append(allErrs, validateScaleIOPersistentVolumeSource(pv.Spec.ScaleIO, specPath.Child("scaleIO"))...)
}
}
if pv.Spec.Local != nil {
@ -1528,6 +1554,12 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
func ValidatePersistentVolumeUpdate(newPv, oldPv *api.PersistentVolume) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = ValidatePersistentVolume(newPv)
// PersistentVolumeSource should be immutable after creation.
if !apiequality.Semantic.DeepEqual(newPv.Spec.PersistentVolumeSource, oldPv.Spec.PersistentVolumeSource) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "persistentvolumesource"), "is immutable after creation"))
}
newPv.Status = oldPv.Status
return allErrs
}
@ -1755,7 +1787,7 @@ func validateObjectFieldSelector(fs *api.ObjectFieldSelector, expressions *sets.
} else if len(fs.FieldPath) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("fieldPath"), ""))
} else {
internalFieldPath, _, err := api.Scheme.ConvertFieldLabel(fs.APIVersion, "Pod", fs.FieldPath, "")
internalFieldPath, _, err := legacyscheme.Scheme.ConvertFieldLabel(fs.APIVersion, "Pod", fs.FieldPath, "")
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("fieldPath"), fs.FieldPath, fmt.Sprintf("error converting fieldPath: %v", err)))
} else if !expressions.Has(internalFieldPath) {
@ -1923,7 +1955,11 @@ func ValidateVolumeMounts(mounts []api.VolumeMount, volumes sets.String, contain
allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be unique"))
}
if !path.IsAbs(mnt.MountPath) {
allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be an absolute path"))
// also allow windows absolute path
p := mnt.MountPath
if len(p) < 2 || ((p[0] < 'A' || p[0] > 'Z') && (p[0] < 'a' || p[0] > 'z')) || p[1] != ':' {
allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be an absolute path"))
}
}
mountpoints.Insert(mnt.MountPath)
if len(mnt.SubPath) > 0 {

View file

@ -571,6 +571,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
in.(*QuobyteVolumeSource).DeepCopyInto(out.(*QuobyteVolumeSource))
return nil
}, InType: reflect.TypeOf(&QuobyteVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*RBDPersistentVolumeSource).DeepCopyInto(out.(*RBDPersistentVolumeSource))
return nil
}, InType: reflect.TypeOf(&RBDPersistentVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*RBDVolumeSource).DeepCopyInto(out.(*RBDVolumeSource))
return nil
@ -627,6 +631,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
in.(*SELinuxOptions).DeepCopyInto(out.(*SELinuxOptions))
return nil
}, InType: reflect.TypeOf(&SELinuxOptions{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*ScaleIOPersistentVolumeSource).DeepCopyInto(out.(*ScaleIOPersistentVolumeSource))
return nil
}, InType: reflect.TypeOf(&ScaleIOPersistentVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*ScaleIOVolumeSource).DeepCopyInto(out.(*ScaleIOVolumeSource))
return nil
@ -3747,7 +3755,7 @@ func (in *PersistentVolumeSource) DeepCopyInto(out *PersistentVolumeSource) {
if *in == nil {
*out = nil
} else {
*out = new(RBDVolumeSource)
*out = new(RBDPersistentVolumeSource)
(*in).DeepCopyInto(*out)
}
}
@ -3864,7 +3872,7 @@ func (in *PersistentVolumeSource) DeepCopyInto(out *PersistentVolumeSource) {
if *in == nil {
*out = nil
} else {
*out = new(ScaleIOVolumeSource)
*out = new(ScaleIOPersistentVolumeSource)
(*in).DeepCopyInto(*out)
}
}
@ -4815,6 +4823,36 @@ func (in *QuobyteVolumeSource) DeepCopy() *QuobyteVolumeSource {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RBDPersistentVolumeSource) DeepCopyInto(out *RBDPersistentVolumeSource) {
*out = *in
if in.CephMonitors != nil {
in, out := &in.CephMonitors, &out.CephMonitors
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
if *in == nil {
*out = nil
} else {
*out = new(SecretReference)
**out = **in
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RBDPersistentVolumeSource.
func (in *RBDPersistentVolumeSource) DeepCopy() *RBDPersistentVolumeSource {
if in == nil {
return nil
}
out := new(RBDPersistentVolumeSource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RBDVolumeSource) DeepCopyInto(out *RBDVolumeSource) {
*out = *in
@ -5196,6 +5234,31 @@ func (in *SELinuxOptions) DeepCopy() *SELinuxOptions {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ScaleIOPersistentVolumeSource) DeepCopyInto(out *ScaleIOPersistentVolumeSource) {
*out = *in
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
if *in == nil {
*out = nil
} else {
*out = new(SecretReference)
**out = **in
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScaleIOPersistentVolumeSource.
func (in *ScaleIOPersistentVolumeSource) DeepCopy() *ScaleIOPersistentVolumeSource {
if in == nil {
return nil
}
out := new(ScaleIOPersistentVolumeSource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ScaleIOVolumeSource) DeepCopyInto(out *ScaleIOVolumeSource) {
*out = *in
@ -5903,7 +5966,15 @@ func (in *TCPSocketAction) DeepCopy() *TCPSocketAction {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Taint) DeepCopyInto(out *Taint) {
*out = *in
in.TimeAdded.DeepCopyInto(&out.TimeAdded)
if in.TimeAdded != nil {
in, out := &in.TimeAdded, &out.TimeAdded
if *in == nil {
*out = nil
} else {
*out = new(v1.Time)
(*in).DeepCopyInto(*out)
}
}
return
}

View file

@ -43,7 +43,7 @@ var (
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to api.Scheme.
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
// TODO this gets cleaned up when the types are fixed
scheme.AddKnownTypes(SchemeGroupVersion,

View file

@ -759,7 +759,7 @@ type IngressBackend struct {
// +genclient:method=UpdateScale,verb=update,subresource=scale,input=Scale,result=Scale
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ReplicaSet represents the configuration of a replica set.
// ReplicaSet ensures that a specified number of pod replicas are running at any given time.
type ReplicaSet struct {
metav1.TypeMeta
// +optional
@ -1010,7 +1010,7 @@ type SELinuxStrategyOptions struct {
// Rule is the strategy that will dictate the allowable labels that may be set.
Rule SELinuxStrategy
// seLinuxOptions required to run as; required for MustRunAs
// More info: https://git.k8s.io/community/contributors/design-proposals/security_context.md
// More info: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#selinux
// +optional
SELinuxOptions *api.SELinuxOptions
}

View file

@ -4,7 +4,7 @@
The mechanism for supporting cloud providers is currently in transition: the original method of implementing cloud provider-specific functionality within the main kubernetes tree (here) is no longer advised; however, the proposed solution is still in development.
#### Guidance for potential cloud providers:
* Support for cloud providers is currently in a state of flux. Background information on motivation and the proposal for improving is in the github [proposal](https://git.k8s.io/community/contributors/design-proposals/cloud-provider-refactoring.md).
* Support for cloud providers is currently in a state of flux. Background information on motivation and the proposal for improving is in the github [proposal](https://git.k8s.io/community/contributors/design-proposals/cloud-provider/cloud-provider-refactoring.md).
* In support of this plan, a new cloud-controller-manager binary was added in 1.6. This was the first of several steps (see the proposal for more information).
* Attempts to contribute new cloud providers or (to a lesser extent) persistent volumes to the core repo will likely meet with some pushback from reviewers/approvers.
* It is understood that this is an unfortunate situation in which 'the old way is no longer supported but the new way is not ready yet', but the initial path is unsustainable, and contributors are encouraged to participate in the implementation of the proposed long-term solution, as there is risk that PRs for new cloud providers here will not be approved.
@ -13,4 +13,4 @@ The mechanism for supporting cloud providers is currently in transition: the or
#### Some additional context on status / direction:
* 1.6 added a new cloud-controller-manager binary that may be used for testing the new out-of-core cloudprovider flow.
* Setting cloud-provider=external allows for creation of a separate controller-manager binary
* 1.7 adds [extensible admission control](https://git.k8s.io/community/contributors/design-proposals/admission_control_extension.md), further enabling topology customization.
* 1.7 adds [extensible admission control](https://git.k8s.io/community/contributors/design-proposals/api-machinery/admission_control_extension.md), further enabling topology customization.

View file

@ -176,6 +176,7 @@ type Routes interface {
var (
InstanceNotFound = errors.New("instance not found")
DiskNotFound = errors.New("disk is not found")
NotImplemented = errors.New("unimplemented")
)
// Zone represents the location of a particular machine.

View file

@ -35,6 +35,7 @@ import (
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/serviceaccount"
"github.com/golang/glog"
@ -237,7 +238,7 @@ func (b SAControllerClientBuilder) getAuthenticatedConfig(sa *v1.ServiceAccount,
// If we couldn't run the token review, the API might be disabled or we might not have permission.
// Try to make a request to /apis with the token. If we get a 401 we should consider the token invalid.
clientConfigCopy := *clientConfig
clientConfigCopy.NegotiatedSerializer = api.Codecs
clientConfigCopy.NegotiatedSerializer = legacyscheme.Codecs
client, err := restclient.UnversionedRESTClientFor(&clientConfigCopy)
if err != nil {
return nil, false, err

View file

@ -38,10 +38,8 @@ import (
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
ref "k8s.io/client-go/tools/reference"
"k8s.io/client-go/util/integer"
clientretry "k8s.io/client-go/util/retry"
_ "k8s.io/kubernetes/pkg/api/install"
@ -474,29 +472,12 @@ func getPodsFinalizers(template *v1.PodTemplateSpec) []string {
return desiredFinalizers
}
func getPodsAnnotationSet(template *v1.PodTemplateSpec, object runtime.Object) (labels.Set, error) {
func getPodsAnnotationSet(template *v1.PodTemplateSpec) labels.Set {
desiredAnnotations := make(labels.Set)
for k, v := range template.Annotations {
desiredAnnotations[k] = v
}
createdByRef, err := ref.GetReference(scheme.Scheme, object)
if err != nil {
return desiredAnnotations, fmt.Errorf("unable to get controller reference: %v", err)
}
// TODO: this code was not safe previously - as soon as new code came along that switched to v2, old clients
// would be broken upon reading it. This is explicitly hardcoded to v1 to guarantee predictable deployment.
// We need to consistently handle this case of annotation versioning.
codec := scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion)
createdByRefJson, err := runtime.Encode(codec, &v1.SerializedReference{
Reference: *createdByRef,
})
if err != nil {
return desiredAnnotations, fmt.Errorf("unable to serialize controller reference: %v", err)
}
desiredAnnotations[v1.CreatedByAnnotation] = string(createdByRefJson)
return desiredAnnotations, nil
return desiredAnnotations
}
func getPodsPrefix(controllerName string) string {
@ -546,17 +527,14 @@ func (r RealPodControl) CreatePodsOnNode(nodeName, namespace string, template *v
}
func (r RealPodControl) PatchPod(namespace, name string, data []byte) error {
_, err := r.KubeClient.Core().Pods(namespace).Patch(name, types.StrategicMergePatchType, data)
_, err := r.KubeClient.CoreV1().Pods(namespace).Patch(name, types.StrategicMergePatchType, data)
return err
}
func GetPodFromTemplate(template *v1.PodTemplateSpec, parentObject runtime.Object, controllerRef *metav1.OwnerReference) (*v1.Pod, error) {
desiredLabels := getPodsLabelSet(template)
desiredFinalizers := getPodsFinalizers(template)
desiredAnnotations, err := getPodsAnnotationSet(template, parentObject)
if err != nil {
return nil, err
}
desiredAnnotations := getPodsAnnotationSet(template)
accessor, err := meta.Accessor(parentObject)
if err != nil {
return nil, fmt.Errorf("parentObject does not have ObjectMeta, %v", err)
@ -589,7 +567,7 @@ func (r RealPodControl) createPods(nodeName, namespace string, template *v1.PodT
if labels.Set(pod.Labels).AsSelectorPreValidated().Empty() {
return fmt.Errorf("unable to create pods, no labels")
}
if newPod, err := r.KubeClient.Core().Pods(namespace).Create(pod); err != nil {
if newPod, err := r.KubeClient.CoreV1().Pods(namespace).Create(pod); err != nil {
r.Recorder.Eventf(object, v1.EventTypeWarning, FailedCreatePodReason, "Error creating: %v", err)
return err
} else {
@ -610,7 +588,7 @@ func (r RealPodControl) DeletePod(namespace string, podID string, object runtime
return fmt.Errorf("object does not have ObjectMeta, %v", err)
}
glog.V(2).Infof("Controller %v deleting pod %v/%v", accessor.GetName(), namespace, podID)
if err := r.KubeClient.Core().Pods(namespace).Delete(podID, nil); err != nil {
if err := r.KubeClient.CoreV1().Pods(namespace).Delete(podID, nil); err != nil {
r.Recorder.Eventf(object, v1.EventTypeWarning, FailedDeletePodReason, "Error deleting: %v", err)
return fmt.Errorf("unable to delete pods: %v", err)
} else {
@ -925,10 +903,10 @@ func AddOrUpdateTaintOnNode(c clientset.Interface, nodeName string, taints ...*v
// First we try getting node from the API server cache, as it's cheaper. If it fails
// we get it from etcd to be sure to have fresh data.
if firstTry {
oldNode, err = c.Core().Nodes().Get(nodeName, metav1.GetOptions{ResourceVersion: "0"})
oldNode, err = c.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{ResourceVersion: "0"})
firstTry = false
} else {
oldNode, err = c.Core().Nodes().Get(nodeName, metav1.GetOptions{})
oldNode, err = c.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
}
if err != nil {
return err
@ -982,10 +960,10 @@ func RemoveTaintOffNode(c clientset.Interface, nodeName string, node *v1.Node, t
// First we try getting node from the API server cache, as it's cheaper. If it fails
// we get it from etcd to be sure to have fresh data.
if firstTry {
oldNode, err = c.Core().Nodes().Get(nodeName, metav1.GetOptions{ResourceVersion: "0"})
oldNode, err = c.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{ResourceVersion: "0"})
firstTry = false
} else {
oldNode, err = c.Core().Nodes().Get(nodeName, metav1.GetOptions{})
oldNode, err = c.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
}
if err != nil {
return err
@ -1030,7 +1008,7 @@ func PatchNodeTaints(c clientset.Interface, nodeName string, oldNode *v1.Node, n
return fmt.Errorf("failed to create patch for node %q: %v", nodeName, err)
}
_, err = c.Core().Nodes().Patch(string(nodeName), types.StrategicMergePatchType, patchBytes)
_, err = c.CoreV1().Nodes().Patch(string(nodeName), types.StrategicMergePatchType, patchBytes)
return err
}

View file

@ -163,6 +163,12 @@ const (
//
// Enable pods to consume pre-allocated huge pages of varying page sizes
HugePages utilfeature.Feature = "HugePages"
// owner @brendandburns
// alpha: v1.8
//
// Enable nodes to exclude themselves from service load balancers
ServiceNodeExclusion utilfeature.Feature = "ServiceNodeExclusion"
)
func init() {
@ -194,6 +200,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
MountPropagation: {Default: false, PreRelease: utilfeature.Alpha},
ExpandPersistentVolumes: {Default: false, PreRelease: utilfeature.Alpha},
CPUManager: {Default: false, PreRelease: utilfeature.Alpha},
ServiceNodeExclusion: {Default: false, PreRelease: utilfeature.Alpha},
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
// unintentionally on either side:
@ -201,7 +208,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
genericfeatures.AdvancedAuditing: {Default: true, PreRelease: utilfeature.Beta},
genericfeatures.APIResponseCompression: {Default: false, PreRelease: utilfeature.Alpha},
genericfeatures.Initializers: {Default: false, PreRelease: utilfeature.Alpha},
genericfeatures.APIListChunking: {Default: false, PreRelease: utilfeature.Alpha},
genericfeatures.APIListChunking: {Default: true, PreRelease: utilfeature.Beta},
// inherited features from apiextensions-apiserver, relisted here to get a conflict if it is changed
// unintentionally on either side:

File diff suppressed because it is too large Load diff

View file

@ -331,6 +331,8 @@ message RemovePodSandboxResponse {}
message PodSandboxStatusRequest {
// ID of the PodSandbox for which to retrieve status.
string pod_sandbox_id = 1;
// Verbose indicates whether to return extra information about the pod sandbox.
bool verbose = 2;
}
// PodSandboxNetworkStatus is the status of the network for a PodSandbox.
@ -382,6 +384,11 @@ message PodSandboxStatus {
message PodSandboxStatusResponse {
// Status of the PodSandbox.
PodSandboxStatus status = 1;
// Info is extra information of the PodSandbox. The key could be abitrary string, and
// value should be in json format. The information could include anything useful for
// debug, e.g. network namespace for linux container based container runtime.
// It should only be returned non-empty when Verbose is true.
map<string, string> info = 2;
}
// PodSandboxStateValue is the wrapper of PodSandboxState.
@ -523,6 +530,7 @@ message LinuxContainerSecurityContext {
repeated int64 supplemental_groups = 8;
// AppArmor profile for the container, candidate values are:
// * runtime/default: equivalent to not specifying a profile.
// * unconfined: no profiles are loaded
// * localhost/<profile_name>: profile loaded on the node
// (localhost) by name. The possible profile names are detailed at
// http://wiki.apparmor.net/index.php/AppArmor_Core_Policy_Reference
@ -746,6 +754,8 @@ message ListContainersResponse {
message ContainerStatusRequest {
// ID of the container for which to retrieve status.
string container_id = 1;
// Verbose indicates whether to return extra information about the container.
bool verbose = 2;
}
// ContainerStatus represents the status of a container.
@ -790,6 +800,11 @@ message ContainerStatus {
message ContainerStatusResponse {
// Status of the container.
ContainerStatus status = 1;
// Info is extra information of the Container. The key could be abitrary string, and
// value should be in json format. The information could include anything useful for
// debug, e.g. pid for linux container based container runtime.
// It should only be returned non-empty when Verbose is true.
map<string, string> info = 2;
}
message UpdateContainerResourcesRequest {
@ -827,7 +842,17 @@ message ExecRequest {
// Whether to exec the command in a TTY.
bool tty = 3;
// Whether to stream stdin.
// One of `stdin`, `stdout`, and `stderr` MUST be true.
bool stdin = 4;
// Whether to stream stdout.
// One of `stdin`, `stdout`, and `stderr` MUST be true.
bool stdout = 5;
// Whether to stream stderr.
// One of `stdin`, `stdout`, and `stderr` MUST be true.
// If `tty` is true, `stderr` MUST be false. Multiplexing is not supported
// in this case. The output of stdout and stderr will be combined to a
// single stream.
bool stderr = 6;
}
message ExecResponse {
@ -839,10 +864,20 @@ message AttachRequest {
// ID of the container to which to attach.
string container_id = 1;
// Whether to stream stdin.
// One of `stdin`, `stdout`, and `stderr` MUST be true.
bool stdin = 2;
// Whether the process being attached is running in a TTY.
// This must match the TTY setting in the ContainerConfig.
bool tty = 3;
// Whether to stream stdout.
// One of `stdin`, `stdout`, and `stderr` MUST be true.
bool stdout = 4;
// Whether to stream stderr.
// One of `stdin`, `stdout`, and `stderr` MUST be true.
// If `tty` is true, `stderr` MUST be false. Multiplexing is not supported
// in this case. The output of stdout and stderr will be combined to a
// single stream.
bool stderr = 5;
}
message AttachResponse {
@ -899,11 +934,18 @@ message ListImagesResponse {
message ImageStatusRequest {
// Spec of the image.
ImageSpec image = 1;
// Verbose indicates whether to return extra information about the image.
bool verbose = 2;
}
message ImageStatusResponse {
// Status of the image.
Image image = 1;
// Info is extra information of the Image. The key could be abitrary string, and
// value should be in json format. The information could include anything useful
// for debug, e.g. image config for oci image based container runtime.
// It should only be returned non-empty when Verbose is true.
map<string, string> info = 2;
}
// AuthConfig contains authorization information for connecting to a registry.
@ -986,11 +1028,19 @@ message RuntimeStatus {
repeated RuntimeCondition conditions = 1;
}
message StatusRequest {}
message StatusRequest {
// Verbose indicates whether to return extra information about the runtime.
bool verbose = 1;
}
message StatusResponse {
// Status of the Runtime.
RuntimeStatus status = 1;
// Info is extra information of the Runtime. The key could be abitrary string, and
// value should be in json format. The information could include anything useful for
// debug, e.g. plugins used by the container runtime.
// It should only be returned non-empty when Verbose is true.
map<string, string> info = 2;
}
message ImageFsInfoRequest {}

View file

@ -48,7 +48,7 @@ type HandlerRunner interface {
type RuntimeHelper interface {
GenerateRunContainerOptions(pod *v1.Pod, container *v1.Container, podIP string) (contOpts *RunContainerOptions, useClusterFirstPolicy bool, err error)
GetClusterDNS(pod *v1.Pod) (dnsServers []string, dnsSearches []string, useClusterFirstPolicy bool, err error)
// GetPodCgroupParent returns the the CgroupName identifer, and its literal cgroupfs form on the host
// GetPodCgroupParent returns the CgroupName identifer, and its literal cgroupfs form on the host
// of a pod.
GetPodCgroupParent(pod *v1.Pod) string
GetPodDir(podUID types.UID) string

View file

@ -21,7 +21,7 @@ import (
"k8s.io/api/core/v1"
ref "k8s.io/client-go/tools/reference"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/legacyscheme"
)
var ImplicitContainerPrefix string = "implicitly required container "
@ -39,7 +39,7 @@ func GenerateContainerRef(pod *v1.Pod, container *v1.Container) (*v1.ObjectRefer
// start (like the pod infra container). This is not a good way, ugh.
fieldPath = ImplicitContainerPrefix + container.Name
}
ref, err := ref.GetPartialReference(api.Scheme, pod, fieldPath)
ref, err := ref.GetPartialReference(legacyscheme.Scheme, pod, fieldPath)
if err != nil {
return nil, err
}

View file

@ -127,9 +127,8 @@ type Runtime interface {
// DirectStreamingRuntime is the interface implemented by runtimes for which the streaming calls
// (exec/attach/port-forward) should be served directly by the Kubelet.
type DirectStreamingRuntime interface {
// Runs the command in the container of the specified pod using nsenter.
// Attaches the processes stdin, stdout, and stderr. Optionally uses a
// tty.
// Runs the command in the container of the specified pod. Attaches
// the processes stdin, stdout, and stderr. Optionally uses a tty.
ExecInContainer(containerID ContainerID, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error
// Forward the specified port from the specified pod to the stream.
PortForward(pod *Pod, port int32, stream io.ReadWriteCloser) error

View file

@ -177,11 +177,6 @@ func (hm *hostportManager) Remove(id string, podPortMapping *PodPortMapping) (er
chainsToRemove := []utiliptables.Chain{}
for _, pm := range hostportMappings {
chainsToRemove = append(chainsToRemove, getHostportChain(id, pm))
// To preserve backward compatibility for k8s 1.5 or earlier.
// Need to remove hostport chains added by hostportSyncer if there is any
// TODO: remove this in 1.7
chainsToRemove = append(chainsToRemove, hostportChainName(pm, getPodFullName(podPortMapping)))
}
// remove rules that consists of target chains

View file

@ -64,7 +64,7 @@ func (hp *hostport) String() string {
return fmt.Sprintf("%s:%d", hp.protocol, hp.port)
}
//openPodHostports opens all hostport for pod and returns the map of hostport and socket
// openHostports opens all hostport for pod and returns the map of hostport and socket
func (h *hostportSyncer) openHostports(podHostportMapping *PodPortMapping) error {
var retErr error
ports := make(map[hostport]closeable)

View file

@ -158,9 +158,24 @@ type server struct {
server *http.Server
}
func (s *server) GetExec(req *runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error) {
func validateExecRequest(req *runtimeapi.ExecRequest) error {
if req.ContainerId == "" {
return nil, grpc.Errorf(codes.InvalidArgument, "missing required container_id")
return grpc.Errorf(codes.InvalidArgument, "missing required container_id")
}
if req.Tty && req.Stderr {
// If TTY is set, stderr cannot be true because multiplexing is not
// supported.
return grpc.Errorf(codes.InvalidArgument, "tty and stderr cannot both be true")
}
if !req.Stdin && !req.Stdout && !req.Stderr {
return grpc.Errorf(codes.InvalidArgument, "one of stdin, stdout, or stderr must be set")
}
return nil
}
func (s *server) GetExec(req *runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error) {
if err := validateExecRequest(req); err != nil {
return nil, err
}
token, err := s.cache.Insert(req)
if err != nil {
@ -171,9 +186,24 @@ func (s *server) GetExec(req *runtimeapi.ExecRequest) (*runtimeapi.ExecResponse,
}, nil
}
func (s *server) GetAttach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error) {
func validateAttachRequest(req *runtimeapi.AttachRequest) error {
if req.ContainerId == "" {
return nil, grpc.Errorf(codes.InvalidArgument, "missing required container_id")
return grpc.Errorf(codes.InvalidArgument, "missing required container_id")
}
if req.Tty && req.Stderr {
// If TTY is set, stderr cannot be true because multiplexing is not
// supported.
return grpc.Errorf(codes.InvalidArgument, "tty and stderr cannot both be true")
}
if !req.Stdin && !req.Stdout && !req.Stderr {
return grpc.Errorf(codes.InvalidArgument, "one of stdin, stdout, and stderr must be set")
}
return nil
}
func (s *server) GetAttach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error) {
if err := validateAttachRequest(req); err != nil {
return nil, err
}
token, err := s.cache.Insert(req)
if err != nil {
@ -239,8 +269,8 @@ func (s *server) serveExec(req *restful.Request, resp *restful.Response) {
streamOpts := &remotecommandserver.Options{
Stdin: exec.Stdin,
Stdout: true,
Stderr: !exec.Tty,
Stdout: exec.Stdout,
Stderr: exec.Stderr,
TTY: exec.Tty,
}
@ -273,8 +303,8 @@ func (s *server) serveAttach(req *restful.Request, resp *restful.Response) {
streamOpts := &remotecommandserver.Options{
Stdin: attach.Stdin,
Stdout: true,
Stderr: !attach.Tty,
Stdout: attach.Stdout,
Stderr: attach.Stderr,
TTY: attach.Tty,
}
remotecommandserver.ServeAttach(
@ -332,11 +362,11 @@ var _ remotecommandserver.Attacher = &criAdapter{}
var _ portforward.PortForwarder = &criAdapter{}
func (a *criAdapter) ExecInContainer(podName string, podUID types.UID, container string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error {
return a.Exec(container, cmd, in, out, err, tty, resize)
return a.Runtime.Exec(container, cmd, in, out, err, tty, resize)
}
func (a *criAdapter) AttachContainer(podName string, podUID types.UID, container string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
return a.Attach(container, in, out, err, tty, resize)
return a.Runtime.Attach(container, in, out, err, tty, resize)
}
func (a *criAdapter) PortForward(podName string, podUID types.UID, port int32, stream io.ReadWriteCloser) error {

View file

@ -47,6 +47,7 @@ import (
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/proxy"
"k8s.io/kubernetes/pkg/proxy/healthcheck"
"k8s.io/kubernetes/pkg/proxy/metrics"
utilproxy "k8s.io/kubernetes/pkg/proxy/util"
"k8s.io/kubernetes/pkg/util/async"
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
@ -166,12 +167,9 @@ type endpointsInfo struct {
chainName utiliptables.Chain
}
// Returns just the IP part of the endpoint.
// IPPart returns just the IP part of the endpoint.
func (e *endpointsInfo) IPPart() string {
if index := strings.Index(e.endpoint, ":"); index != -1 {
return e.endpoint[0:index]
}
return e.endpoint
return utilproxy.IPPart(e.endpoint)
}
// Returns the endpoint chain name for a given endpointsInfo.
@ -320,12 +318,14 @@ func (scm *serviceChangeMap) update(namespacedName *types.NamespacedName, previo
func (sm *proxyServiceMap) merge(other proxyServiceMap) sets.String {
existingPorts := sets.NewString()
for svcPortName, info := range other {
port := strconv.Itoa(info.port)
clusterIPPort := net.JoinHostPort(info.clusterIP.String(), port)
existingPorts.Insert(svcPortName.Port)
_, exists := (*sm)[svcPortName]
if !exists {
glog.V(1).Infof("Adding new service port %q at %s:%d/%s", svcPortName, info.clusterIP, info.port, info.protocol)
glog.V(1).Infof("Adding new service port %q at %s/%s", svcPortName, clusterIPPort, info.protocol)
} else {
glog.V(1).Infof("Updating existing service port %q at %s:%d/%s", svcPortName, info.clusterIP, info.port, info.protocol)
glog.V(1).Infof("Updating existing service port %q at %s/%s", svcPortName, clusterIPPort, info.protocol)
}
(*sm)[svcPortName] = info
}
@ -798,11 +798,15 @@ func getLocalIPs(endpointsMap proxyEndpointsMap) map[types.NamespacedName]sets.S
for svcPortName := range endpointsMap {
for _, ep := range endpointsMap[svcPortName] {
if ep.isLocal {
nsn := svcPortName.NamespacedName
if localIPs[nsn] == nil {
localIPs[nsn] = sets.NewString()
// If the endpoint has a bad format, ipPart() will log an
// error and ep.IPPart() will return a null string.
if ip := ep.IPPart(); ip != "" {
nsn := svcPortName.NamespacedName
if localIPs[nsn] == nil {
localIPs[nsn] = sets.NewString()
}
localIPs[nsn].Insert(ip)
}
localIPs[nsn].Insert(ep.IPPart()) // just the IP part
}
}
}
@ -924,10 +928,7 @@ type endpointServicePair struct {
}
func (esp *endpointServicePair) IPPart() string {
if index := strings.Index(esp.endpoint, ":"); index != -1 {
return esp.endpoint[0:index]
}
return esp.endpoint
return utilproxy.IPPart(esp.endpoint)
}
// After a UDP endpoint has been removed, we must flush any pending conntrack entries to it, or else we
@ -936,7 +937,7 @@ func (esp *endpointServicePair) IPPart() string {
func (proxier *Proxier) deleteEndpointConnections(connectionMap map[endpointServicePair]bool) {
for epSvcPair := range connectionMap {
if svcInfo, ok := proxier.serviceMap[epSvcPair.servicePortName]; ok && svcInfo.protocol == api.ProtocolUDP {
endpointIP := epSvcPair.endpoint[0:strings.Index(epSvcPair.endpoint, ":")]
endpointIP := utilproxy.IPPart(epSvcPair.endpoint)
err := utilproxy.ClearUDPConntrackForPeers(proxier.exec, svcInfo.clusterIP.String(), endpointIP)
if err != nil {
glog.Errorf("Failed to delete %s endpoint connections, error: %v", epSvcPair.servicePortName.String(), err)
@ -954,7 +955,7 @@ func (proxier *Proxier) syncProxyRules() {
start := time.Now()
defer func() {
SyncProxyRulesLatency.Observe(sinceInMicroseconds(start))
metrics.SyncProxyRulesLatency.Observe(metrics.SinceInMicroseconds(start))
glog.V(4).Infof("syncProxyRules took %v", time.Since(start))
}()
// don't sync rules till we've received services and endpoints
@ -1162,7 +1163,7 @@ func (proxier *Proxier) syncProxyRules() {
"-A", string(kubeServicesChain),
"-m", "comment", "--comment", fmt.Sprintf(`"%s cluster IP"`, svcNameString),
"-m", protocol, "-p", protocol,
"-d", fmt.Sprintf("%s/32", svcInfo.clusterIP.String()),
"-d", utilproxy.ToCIDR(svcInfo.clusterIP),
"--dport", strconv.Itoa(svcInfo.port),
)
if proxier.masqueradeAll {
@ -1216,7 +1217,7 @@ func (proxier *Proxier) syncProxyRules() {
"-A", string(kubeServicesChain),
"-m", "comment", "--comment", fmt.Sprintf(`"%s external IP"`, svcNameString),
"-m", protocol, "-p", protocol,
"-d", fmt.Sprintf("%s/32", externalIP),
"-d", utilproxy.ToCIDR(net.ParseIP(externalIP)),
"--dport", strconv.Itoa(svcInfo.port),
)
// We have to SNAT packets to external IPs.
@ -1242,7 +1243,7 @@ func (proxier *Proxier) syncProxyRules() {
"-A", string(kubeServicesChain),
"-m", "comment", "--comment", fmt.Sprintf(`"%s has no endpoints"`, svcNameString),
"-m", protocol, "-p", protocol,
"-d", fmt.Sprintf("%s/32", externalIP),
"-d", utilproxy.ToCIDR(net.ParseIP(externalIP)),
"--dport", strconv.Itoa(svcInfo.port),
"-j", "REJECT",
)
@ -1268,7 +1269,7 @@ func (proxier *Proxier) syncProxyRules() {
"-A", string(kubeServicesChain),
"-m", "comment", "--comment", fmt.Sprintf(`"%s loadbalancer IP"`, svcNameString),
"-m", protocol, "-p", protocol,
"-d", fmt.Sprintf("%s/32", ingress.IP),
"-d", utilproxy.ToCIDR(net.ParseIP(ingress.IP)),
"--dport", strconv.Itoa(svcInfo.port),
)
// jump to service firewall chain
@ -1306,7 +1307,7 @@ func (proxier *Proxier) syncProxyRules() {
// loadbalancer's backend hosts. In this case, request will not hit the loadbalancer but loop back directly.
// Need to add the following rule to allow request on host.
if allowFromNode {
writeLine(proxier.natRules, append(args, "-s", fmt.Sprintf("%s/32", ingress.IP), "-j", string(chosenChain))...)
writeLine(proxier.natRules, append(args, "-s", utilproxy.ToCIDR(net.ParseIP(ingress.IP)), "-j", string(chosenChain))...)
}
}
@ -1342,7 +1343,8 @@ func (proxier *Proxier) syncProxyRules() {
// This is very low impact. The NodePort range is intentionally obscure, and unlikely to actually collide with real Services.
// This only affects UDP connections, which are not common.
// See issue: https://github.com/kubernetes/kubernetes/issues/49881
err := utilproxy.ClearUDPConntrackForPort(proxier.exec, lp.Port)
isIPv6 := utilproxy.IsIPv6(svcInfo.clusterIP)
err := utilproxy.ClearUDPConntrackForPort(proxier.exec, lp.Port, isIPv6)
if err != nil {
glog.Errorf("Failed to clear udp conntrack for port %d, error: %v", lp.Port, err)
}
@ -1389,7 +1391,7 @@ func (proxier *Proxier) syncProxyRules() {
"-A", string(kubeServicesChain),
"-m", "comment", "--comment", fmt.Sprintf(`"%s has no endpoints"`, svcNameString),
"-m", protocol, "-p", protocol,
"-d", fmt.Sprintf("%s/32", svcInfo.clusterIP.String()),
"-d", utilproxy.ToCIDR(svcInfo.clusterIP),
"--dport", strconv.Itoa(svcInfo.port),
"-j", "REJECT",
)
@ -1433,6 +1435,11 @@ func (proxier *Proxier) syncProxyRules() {
// Now write loadbalancing & DNAT rules.
n := len(endpointChains)
for i, endpointChain := range endpointChains {
epIP := endpoints[i].IPPart()
if epIP == "" {
// Error parsing this endpoint has been logged. Skip to next endpoint.
continue
}
// Balancing rules in the per-service chain.
args = append(args[:0], []string{
"-A", string(svcChain),
@ -1456,7 +1463,7 @@ func (proxier *Proxier) syncProxyRules() {
)
// Handle traffic that loops back to the originator with SNAT.
writeLine(proxier.natRules, append(args,
"-s", fmt.Sprintf("%s/32", endpoints[i].IPPart()),
"-s", utilproxy.ToCIDR(net.ParseIP(epIP)),
"-j", string(KubeMarkMasqChain))...)
// Update client-affinity lists.
if svcInfo.sessionAffinityType == api.ServiceAffinityClientIP {
@ -1571,20 +1578,6 @@ func (proxier *Proxier) syncProxyRules() {
err = proxier.iptables.RestoreAll(proxier.iptablesData.Bytes(), utiliptables.NoFlushTables, utiliptables.RestoreCounters)
if err != nil {
glog.Errorf("Failed to execute iptables-restore: %v", err)
// ~rough approximation, assume ~100 chars per line
// we log first 1000 bytes, but full list at higher levels
rules := proxier.iptablesData.Bytes()
if len(rules) > 1000 {
abridgedRules := rules[:1000]
if glog.V(4) {
glog.V(4).Infof("Rules:\n%s", rules)
} else {
glog.V(2).Infof("Rules (abridged):\n%s", abridgedRules)
}
} else {
glog.V(2).Infof("Rules:\n%s", rules)
}
// Revert new local ports.
glog.V(2).Infof("Closing local ports after iptables-restore failure")
utilproxy.RevertPorts(replacementPortsMap, proxier.portsMap)

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package iptables
package metrics
import (
"sync"
@ -26,6 +26,7 @@ import (
const kubeProxySubsystem = "kubeproxy"
var (
// SyncProxyRulesLatency is the latency of one round of kube-proxy syncing proxy rules.
SyncProxyRulesLatency = prometheus.NewHistogram(
prometheus.HistogramOpts{
Subsystem: kubeProxySubsystem,
@ -38,13 +39,14 @@ var (
var registerMetricsOnce sync.Once
// RegisterMetrics registers sync proxy rules latency metrics
func RegisterMetrics() {
registerMetricsOnce.Do(func() {
prometheus.MustRegister(SyncProxyRulesLatency)
})
}
// Gets the time since the specified start in microseconds.
func sinceInMicroseconds(start time.Time) float64 {
// SinceInMicroseconds gets the time since the specified start in microseconds.
func SinceInMicroseconds(start time.Time) float64 {
return float64(time.Since(start).Nanoseconds() / time.Microsecond.Nanoseconds())
}

View file

@ -18,6 +18,7 @@ package util
import (
"fmt"
"net"
"strconv"
"strings"
@ -28,10 +29,27 @@ import (
const noConnectionToDelete = "0 flow entries have been deleted"
// DeleteServiceConnections uses the conntrack tool to delete the conntrack entries
func IsIPv6(netIP net.IP) bool {
return netIP != nil && netIP.To4() == nil
}
func IsIPv6String(ip string) bool {
netIP := net.ParseIP(ip)
return IsIPv6(netIP)
}
func parametersWithFamily(isIPv6 bool, parameters ...string) []string {
if isIPv6 {
parameters = append(parameters, "-f", "ipv6")
}
return parameters
}
// ClearUDPConntrackForIP uses the conntrack tool to delete the conntrack entries
// for the UDP connections specified by the given service IP
func ClearUDPConntrackForIP(execer exec.Interface, ip string) error {
err := ExecConntrackTool(execer, "-D", "--orig-dst", ip, "-p", "udp")
parameters := parametersWithFamily(IsIPv6String(ip), "-D", "--orig-dst", ip, "-p", "udp")
err := ExecConntrackTool(execer, parameters...)
if err != nil && !strings.Contains(err.Error(), noConnectionToDelete) {
// TODO: Better handling for deletion failure. When failure occur, stale udp connection may not get flushed.
// These stale udp connection will keep black hole traffic. Making this a best effort operation for now, since it
@ -60,11 +78,12 @@ func ExecConntrackTool(execer exec.Interface, parameters ...string) error {
// The solution is clearing the conntrack. Known issues:
// https://github.com/docker/docker/issues/8795
// https://github.com/kubernetes/kubernetes/issues/31983
func ClearUDPConntrackForPort(execer exec.Interface, port int) error {
func ClearUDPConntrackForPort(execer exec.Interface, port int, isIPv6 bool) error {
if port <= 0 {
return fmt.Errorf("Wrong port number. The port number must be greater than zero")
}
err := ExecConntrackTool(execer, "-D", "-p", "udp", "--dport", strconv.Itoa(port))
parameters := parametersWithFamily(isIPv6, "-D", "-p", "udp", "--dport", strconv.Itoa(port))
err := ExecConntrackTool(execer, parameters...)
if err != nil && !strings.Contains(err.Error(), noConnectionToDelete) {
return fmt.Errorf("error deleting conntrack entries for UDP port: %d, error: %v", port, err)
}
@ -74,7 +93,8 @@ func ClearUDPConntrackForPort(execer exec.Interface, port int) error {
// ClearUDPConntrackForPeers uses the conntrack tool to delete the conntrack entries
// for the UDP connections specified by the {origin, dest} IP pair.
func ClearUDPConntrackForPeers(execer exec.Interface, origin, dest string) error {
err := ExecConntrackTool(execer, "-D", "--orig-dst", origin, "--dst-nat", dest, "-p", "udp")
parameters := parametersWithFamily(IsIPv6String(origin), "-D", "--orig-dst", origin, "--dst-nat", dest, "-p", "udp")
err := ExecConntrackTool(execer, parameters...)
if err != nil && !strings.Contains(err.Error(), noConnectionToDelete) {
// TODO: Better handling for deletion failure. When failure occur, stale udp connection may not get flushed.
// These stale udp connection will keep black hole traffic. Making this a best effort operation for now, since it

51
vendor/k8s.io/kubernetes/pkg/proxy/util/endpoints.go generated vendored Normal file
View file

@ -0,0 +1,51 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"fmt"
"net"
"github.com/golang/glog"
)
// IPPart returns just the IP part of an IP or IP:port or endpoint string. If the IP
// part is an IPv6 address enclosed in brackets (e.g. "[fd00:1::5]:9999"),
// then the brackets are stripped as well.
func IPPart(s string) string {
if ip := net.ParseIP(s); ip != nil {
// IP address without port
return s
}
// Must be IP:port
ip, _, err := net.SplitHostPort(s)
if err != nil {
glog.Errorf("Error parsing '%s': %v", s, err)
return ""
}
return ip
}
// ToCIDR returns a host address of the form <ip-address>/32 for
// IPv4 and <ip-address>/128 for IPv6
func ToCIDR(ip net.IP) string {
len := 32
if ip.To4() == nil {
len = 128
}
return fmt.Sprintf("%s/%d", ip.String(), len)
}

View file

@ -18,6 +18,8 @@ package util
import (
"fmt"
"net"
"strconv"
"github.com/golang/glog"
)
@ -37,7 +39,8 @@ type LocalPort struct {
}
func (lp *LocalPort) String() string {
return fmt.Sprintf("%q (%s:%d/%s)", lp.Description, lp.IP, lp.Port, lp.Protocol)
ipPort := net.JoinHostPort(lp.IP, strconv.Itoa(lp.Port))
return fmt.Sprintf("%q (%s/%s)", lp.Description, ipPort, lp.Protocol)
}
// Closeable is an interface around closing an port.

View file

@ -35,13 +35,16 @@ const (
ProfileRuntimeDefault = "runtime/default"
// The prefix for specifying profiles loaded on the node.
ProfileNamePrefix = "localhost/"
// Unconfined profile
ProfileNameUnconfined = "unconfined"
)
// Checks whether app armor is required for pod to be run.
func isRequired(pod *v1.Pod) bool {
for key := range pod.Annotations {
for key, value := range pod.Annotations {
if strings.HasPrefix(key, ContainerAnnotationKeyPrefix) {
return true
return value != ProfileNameUnconfined
}
}
return false

View file

@ -136,7 +136,7 @@ func validateProfile(profile string, loadedProfiles map[string]bool) error {
}
func ValidateProfileFormat(profile string) error {
if profile == "" || profile == ProfileRuntimeDefault {
if profile == "" || profile == ProfileRuntimeDefault || profile == ProfileNameUnconfined {
return nil
}
if !strings.HasPrefix(profile, ProfileNamePrefix) {

View file

@ -18,6 +18,7 @@ package dbus
import (
"fmt"
"sync"
godbus "github.com/godbus/dbus"
)
@ -30,6 +31,7 @@ type DBusFake struct {
// DBusFakeConnection represents a fake D-Bus connection
type DBusFakeConnection struct {
lock sync.Mutex
busObject *fakeObject
objects map[string]*fakeObject
signalHandlers []chan<- *godbus.Signal
@ -88,6 +90,8 @@ func (conn *DBusFakeConnection) Object(name, path string) Object {
// Signal is part of the Connection interface
func (conn *DBusFakeConnection) Signal(ch chan<- *godbus.Signal) {
conn.lock.Lock()
defer conn.lock.Unlock()
for i := range conn.signalHandlers {
if conn.signalHandlers[i] == ch {
conn.signalHandlers = append(conn.signalHandlers[:i], conn.signalHandlers[i+1:]...)
@ -109,6 +113,8 @@ func (conn *DBusFakeConnection) AddObject(name, path string, handler DBusFakeHan
// EmitSignal emits a signal on conn
func (conn *DBusFakeConnection) EmitSignal(name, path, iface, signal string, args ...interface{}) {
conn.lock.Lock()
defer conn.lock.Unlock()
sig := &godbus.Signal{
Sender: name,
Path: godbus.ObjectPath(path),

View file

@ -94,10 +94,12 @@ const (
)
const (
cmdIPTablesSave string = "iptables-save"
cmdIPTablesRestore string = "iptables-restore"
cmdIPTables string = "iptables"
cmdIp6tables string = "ip6tables"
cmdIPTablesSave string = "iptables-save"
cmdIPTablesRestore string = "iptables-restore"
cmdIPTables string = "iptables"
cmdIP6TablesRestore string = "ip6tables-restore"
cmdIP6TablesSave string = "ip6tables-save"
cmdIP6Tables string = "ip6tables"
)
// Option flag for Restore
@ -116,9 +118,11 @@ const NoFlushTables FlushFlag = false
// (test whether a rule exists).
const MinCheckVersion = "1.4.11"
// Minimum iptables versions supporting the -w and -w2 flags
const MinWaitVersion = "1.4.20"
const MinWait2Version = "1.4.22"
// Minimum iptables versions supporting the -w and -w<seconds> flags
const WaitMinVersion = "1.4.20"
const WaitSecondsMinVersion = "1.4.22"
const WaitString = "-w"
const WaitSecondsString = "-w5"
const LockfilePath16x = "/run/xtables.lock"
@ -140,7 +144,7 @@ type runner struct {
// newInternal returns a new Interface which will exec iptables, and allows the
// caller to change the iptables-restore lockfile path
func newInternal(exec utilexec.Interface, dbus utildbus.Interface, protocol Protocol, lockfilePath string) Interface {
vstring, err := getIPTablesVersionString(exec)
vstring, err := getIPTablesVersionString(exec, protocol)
if err != nil {
glog.Warningf("Error checking iptables version, assuming version at least %s: %v", MinCheckVersion, err)
vstring = MinCheckVersion
@ -156,7 +160,7 @@ func newInternal(exec utilexec.Interface, dbus utildbus.Interface, protocol Prot
protocol: protocol,
hasCheck: getIPTablesHasCheckCommand(vstring),
waitFlag: getIPTablesWaitFlag(vstring),
restoreWaitFlag: getIPTablesRestoreWaitFlag(exec),
restoreWaitFlag: getIPTablesRestoreWaitFlag(exec, protocol),
lockfilePath: lockfilePath,
}
// TODO this needs to be moved to a separate Start() or Run() function so that New() has zero side
@ -207,7 +211,7 @@ func (runner *runner) connectToFirewallD() {
// GetVersion returns the version string.
func (runner *runner) GetVersion() (string, error) {
return getIPTablesVersionString(runner.exec)
return getIPTablesVersionString(runner.exec, runner.protocol)
}
// EnsureChain is part of Interface.
@ -310,9 +314,10 @@ func (runner *runner) SaveInto(table Table, buffer *bytes.Buffer) error {
defer runner.mu.Unlock()
// run and return
iptablesSaveCmd := iptablesSaveCommand(runner.protocol)
args := []string{"-t", string(table)}
glog.V(4).Infof("running iptables-save %v", args)
cmd := runner.exec.Command(cmdIPTablesSave, args...)
glog.V(4).Infof("running %s %v", iptablesSaveCmd, args)
cmd := runner.exec.Command(iptablesSaveCmd, args...)
// Since CombinedOutput() doesn't support redirecting it to a buffer,
// we need to workaround it by redirecting stdout and stderr to buffer
// and explicitly calling Run() [CombinedOutput() underneath itself
@ -370,8 +375,9 @@ func (runner *runner) restoreInternal(args []string, data []byte, flush FlushFla
// run the command and return the output or an error including the output and error
fullArgs := append(runner.restoreWaitFlag, args...)
glog.V(4).Infof("running iptables-restore %v", fullArgs)
cmd := runner.exec.Command(cmdIPTablesRestore, fullArgs...)
iptablesRestoreCmd := iptablesRestoreCommand(runner.protocol)
glog.V(4).Infof("running %s %v", iptablesRestoreCmd, fullArgs)
cmd := runner.exec.Command(iptablesRestoreCmd, fullArgs...)
cmd.SetStdin(bytes.NewBuffer(data))
b, err := cmd.CombinedOutput()
if err != nil {
@ -380,17 +386,32 @@ func (runner *runner) restoreInternal(args []string, data []byte, flush FlushFla
return nil
}
func (runner *runner) iptablesCommand() string {
if runner.IsIpv6() {
return cmdIp6tables
func iptablesSaveCommand(protocol Protocol) string {
if protocol == ProtocolIpv6 {
return cmdIP6TablesSave
} else {
return cmdIPTablesSave
}
}
func iptablesRestoreCommand(protocol Protocol) string {
if protocol == ProtocolIpv6 {
return cmdIP6TablesRestore
} else {
return cmdIPTablesRestore
}
}
func iptablesCommand(protocol Protocol) string {
if protocol == ProtocolIpv6 {
return cmdIP6Tables
} else {
return cmdIPTables
}
}
func (runner *runner) run(op operation, args []string) ([]byte, error) {
iptablesCmd := runner.iptablesCommand()
iptablesCmd := iptablesCommand(runner.protocol)
fullArgs := append(runner.waitFlag, string(op))
fullArgs = append(fullArgs, args...)
glog.V(5).Infof("running iptables %s %v", string(op), args)
@ -418,8 +439,9 @@ func trimhex(s string) string {
// Present for compatibility with <1.4.11 versions of iptables. This is full
// of hack and half-measures. We should nix this ASAP.
func (runner *runner) checkRuleWithoutCheck(table Table, chain Chain, args ...string) (bool, error) {
glog.V(1).Infof("running iptables-save -t %s", string(table))
out, err := runner.exec.Command(cmdIPTablesSave, "-t", string(table)).CombinedOutput()
iptablesSaveCmd := iptablesSaveCommand(runner.protocol)
glog.V(1).Infof("running %s -t %s", iptablesSaveCmd, string(table))
out, err := runner.exec.Command(iptablesSaveCmd, "-t", string(table)).CombinedOutput()
if err != nil {
return false, fmt.Errorf("error checking rule: %v", err)
}
@ -517,32 +539,33 @@ func getIPTablesWaitFlag(vstring string) []string {
return nil
}
minVersion, err := utilversion.ParseGeneric(MinWaitVersion)
minVersion, err := utilversion.ParseGeneric(WaitMinVersion)
if err != nil {
glog.Errorf("MinWaitVersion (%s) is not a valid version string: %v", MinWaitVersion, err)
glog.Errorf("WaitMinVersion (%s) is not a valid version string: %v", WaitMinVersion, err)
return nil
}
if version.LessThan(minVersion) {
return nil
}
minVersion, err = utilversion.ParseGeneric(MinWait2Version)
minVersion, err = utilversion.ParseGeneric(WaitSecondsMinVersion)
if err != nil {
glog.Errorf("MinWait2Version (%s) is not a valid version string: %v", MinWait2Version, err)
glog.Errorf("WaitSecondsMinVersion (%s) is not a valid version string: %v", WaitSecondsMinVersion, err)
return nil
}
if version.LessThan(minVersion) {
return []string{"-w"}
return []string{WaitString}
} else {
return []string{"-w2"}
return []string{WaitSecondsString}
}
}
// getIPTablesVersionString runs "iptables --version" to get the version string
// in the form "X.X.X"
func getIPTablesVersionString(exec utilexec.Interface) (string, error) {
func getIPTablesVersionString(exec utilexec.Interface, protocol Protocol) (string, error) {
// this doesn't access mutable state so we don't need to use the interface / runner
bytes, err := exec.Command(cmdIPTables, "--version").CombinedOutput()
iptablesCmd := iptablesCommand(protocol)
bytes, err := exec.Command(iptablesCmd, "--version").CombinedOutput()
if err != nil {
return "", err
}
@ -558,8 +581,8 @@ func getIPTablesVersionString(exec utilexec.Interface) (string, error) {
// --wait support landed in v1.6.1+ right before --version support, so
// any version of iptables-restore that supports --version will also
// support --wait
func getIPTablesRestoreWaitFlag(exec utilexec.Interface) []string {
vstring, err := getIPTablesRestoreVersionString(exec)
func getIPTablesRestoreWaitFlag(exec utilexec.Interface, protocol Protocol) []string {
vstring, err := getIPTablesRestoreVersionString(exec, protocol)
if err != nil || vstring == "" {
glog.V(3).Infof("couldn't get iptables-restore version; assuming it doesn't support --wait")
return nil
@ -574,13 +597,14 @@ func getIPTablesRestoreWaitFlag(exec utilexec.Interface) []string {
// getIPTablesRestoreVersionString runs "iptables-restore --version" to get the version string
// in the form "X.X.X"
func getIPTablesRestoreVersionString(exec utilexec.Interface) (string, error) {
func getIPTablesRestoreVersionString(exec utilexec.Interface, protocol Protocol) (string, error) {
// this doesn't access mutable state so we don't need to use the interface / runner
// iptables-restore hasn't always had --version, and worse complains
// about unrecognized commands but doesn't exit when it gets them.
// Work around that by setting stdin to nothing so it exits immediately.
cmd := exec.Command(cmdIPTablesRestore, "--version")
iptablesRestoreCmd := iptablesRestoreCommand(protocol)
cmd := exec.Command(iptablesRestoreCmd, "--version")
cmd.SetStdin(bytes.NewReader([]byte{}))
bytes, err := cmd.CombinedOutput()
if err != nil {

View file

@ -17,6 +17,7 @@ limitations under the License.
package mount
import (
"os"
"path/filepath"
"sync"
@ -125,7 +126,7 @@ func (f *FakeMounter) List() ([]MountPoint, error) {
}
func (f *FakeMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
return (mp.Path == dir)
return mp.Path == dir
}
func (f *FakeMounter) IsNotMountPoint(dir string) (bool, error) {
@ -136,6 +137,11 @@ func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
f.mutex.Lock()
defer f.mutex.Unlock()
_, err := os.Stat(file)
if err != nil {
return true, err
}
// If file is a symlink, get its absolute path
absFile, err := filepath.EvalSymlinks(file)
if err != nil {
@ -175,3 +181,19 @@ func (f *FakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (strin
func (f *FakeMounter) MakeRShared(path string) error {
return nil
}
func (f *FakeMounter) GetFileType(pathname string) (FileType, error) {
return FileType("fake"), nil
}
func (f *FakeMounter) MakeDir(pathname string) error {
return nil
}
func (f *FakeMounter) MakeFile(pathname string) error {
return nil
}
func (f *FakeMounter) ExistsPath(pathname string) bool {
return false
}

View file

@ -19,18 +19,20 @@ limitations under the License.
package mount
import (
"fmt"
"path"
"path/filepath"
"strings"
"github.com/golang/glog"
)
type FileType string
const (
// Default mount command if mounter path is not specified
defaultMountCommand = "mount"
MountsInGlobalPDPath = "mounts"
defaultMountCommand = "mount"
MountsInGlobalPDPath = "mounts"
FileTypeDirectory FileType = "Directory"
FileTypeFile FileType = "File"
FileTypeSocket FileType = "Socket"
FileTypeCharDev FileType = "CharDevice"
FileTypeBlockDev FileType = "BlockDevice"
)
type Interface interface {
@ -70,6 +72,18 @@ type Interface interface {
// MakeRShared checks that given path is on a mount with 'rshared' mount
// propagation. If not, it bind-mounts the path as rshared.
MakeRShared(path string) error
// GetFileType checks for file/directory/socket/block/character devices.
// Will operate in the host mount namespace if kubelet is running in a container
GetFileType(pathname string) (FileType, error)
// MakeFile creates an empty file.
// Will operate in the host mount namespace if kubelet is running in a container
MakeFile(pathname string) error
// MakeDir creates a new directory.
// Will operate in the host mount namespace if kubelet is running in a container
MakeDir(pathname string) error
// ExistsPath checks whether the path exists.
// Will operate in the host mount namespace if kubelet is running in a container
ExistsPath(pathname string) bool
}
// Exec executes command where mount utilities are. This can be either the host,
@ -119,41 +133,6 @@ func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string,
return mounter.formatAndMount(source, target, fstype, options)
}
// GetMountRefs finds all other references to the device referenced
// by mountPath; returns a list of paths.
func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
mps, err := mounter.List()
if err != nil {
return nil, err
}
// Find the device name.
deviceName := ""
// If mountPath is symlink, need get its target path.
slTarget, err := filepath.EvalSymlinks(mountPath)
if err != nil {
slTarget = mountPath
}
for i := range mps {
if mps[i].Path == slTarget {
deviceName = mps[i].Device
break
}
}
// Find all references to the device.
var refs []string
if deviceName == "" {
glog.Warningf("could not determine device for path: %q", mountPath)
} else {
for i := range mps {
if mps[i].Device == deviceName && mps[i].Path != slTarget {
refs = append(refs, mps[i].Path)
}
}
}
return refs, nil
}
// GetMountRefsByDev finds all references to the device provided
// by mountPath; returns a list of paths.
func GetMountRefsByDev(mounter Interface, mountPath string) ([]string, error) {
@ -220,34 +199,6 @@ func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, e
return device, refCount, nil
}
// getDeviceNameFromMount find the device name from /proc/mounts in which
// the mount path reference should match the given plugin directory. In case no mount path reference
// matches, returns the volume name taken from its given mountPath
func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
refs, err := GetMountRefs(mounter, mountPath)
if err != nil {
glog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
return "", err
}
if len(refs) == 0 {
glog.V(4).Infof("Directory %s is not mounted", mountPath)
return "", fmt.Errorf("directory %s is not mounted", mountPath)
}
basemountPath := path.Join(pluginDir, MountsInGlobalPDPath)
for _, ref := range refs {
if strings.HasPrefix(ref, basemountPath) {
volumeID, err := filepath.Rel(basemountPath, ref)
if err != nil {
glog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
return "", err
}
return volumeID, nil
}
}
return path.Base(mountPath), nil
}
// IsNotMountPoint determines if a directory is a mountpoint.
// It should return ErrNotExist when the directory does not exist.
// This method uses the List() of all mountpoints

View file

@ -22,6 +22,8 @@ import (
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"syscall"
@ -42,9 +44,6 @@ const (
procMountsPath = "/proc/mounts"
// Location of the mountinfo file
procMountInfoPath = "/proc/self/mountinfo"
)
const (
// 'fsck' found errors and corrected them
fsckErrorsCorrected = 1
// 'fsck' found errors but exited without correcting them
@ -71,12 +70,12 @@ func New(mounterPath string) Interface {
// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
// be an emtpy string in case it's not required, e.g. for remount, or for auto filesystem
// type, where kernel handles fs type for you. The mount 'options' is a list of options,
// type, where kernel handles fstype for you. The mount 'options' is a list of options,
// currently come from mount(8), e.g. "ro", "remount", "bind", etc. If no more option is
// required, call Mount with an empty string list or nil.
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
// Path to mounter binary if containerized mounter is needed. Otherwise, it is set to empty.
// All Linux distros are expected to be shipped with a mount utility that an support bind mounts.
// All Linux distros are expected to be shipped with a mount utility that a support bind mounts.
mounterPath := ""
bind, bindRemountOpts := isBind(options)
if bind {
@ -144,6 +143,41 @@ func (m *Mounter) doMount(mounterPath string, mountCmd string, source string, ta
return err
}
// GetMountRefs finds all other references to the device referenced
// by mountPath; returns a list of paths.
func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
mps, err := mounter.List()
if err != nil {
return nil, err
}
// Find the device name.
deviceName := ""
// If mountPath is symlink, need get its target path.
slTarget, err := filepath.EvalSymlinks(mountPath)
if err != nil {
slTarget = mountPath
}
for i := range mps {
if mps[i].Path == slTarget {
deviceName = mps[i].Device
break
}
}
// Find all references to the device.
var refs []string
if deviceName == "" {
glog.Warningf("could not determine device for path: %q", mountPath)
} else {
for i := range mps {
if mps[i].Device == deviceName && mps[i].Path != slTarget {
refs = append(refs, mps[i].Path)
}
}
}
return refs, nil
}
// detectSystemd returns true if OS runs with systemd as init. When not sure
// (permission errors, ...), it returns false.
// There may be different ways how to detect systemd, this one makes sure that
@ -255,17 +289,29 @@ func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
return pathIsDevice(pathname)
pathType, err := mounter.GetFileType(pathname)
isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
return isDevice, err
}
func exclusiveOpenFailsOnDevice(pathname string) (bool, error) {
isDevice, err := pathIsDevice(pathname)
var isDevice bool
finfo, err := os.Stat(pathname)
if os.IsNotExist(err) {
isDevice = false
}
// err in call to os.Stat
if err != nil {
return false, fmt.Errorf(
"PathIsDevice failed for path %q: %v",
pathname,
err)
}
// path refers to a device
if finfo.Mode()&os.ModeDevice != 0 {
isDevice = true
}
if !isDevice {
glog.Errorf("Path %q is not refering to a device.", pathname)
return false, nil
@ -285,28 +331,39 @@ func exclusiveOpenFailsOnDevice(pathname string) (bool, error) {
return false, errno
}
func pathIsDevice(pathname string) (bool, error) {
finfo, err := os.Stat(pathname)
if os.IsNotExist(err) {
return false, nil
}
// err in call to os.Stat
if err != nil {
return false, err
}
// path refers to a device
if finfo.Mode()&os.ModeDevice != 0 {
return true, nil
}
// path does not refer to device
return false, nil
}
//GetDeviceNameFromMount: given a mount point, find the device name from its global mount point
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
return getDeviceNameFromMount(mounter, mountPath, pluginDir)
}
// getDeviceNameFromMount find the device name from /proc/mounts in which
// the mount path reference should match the given plugin directory. In case no mount path reference
// matches, returns the volume name taken from its given mountPath
func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
refs, err := GetMountRefs(mounter, mountPath)
if err != nil {
glog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
return "", err
}
if len(refs) == 0 {
glog.V(4).Infof("Directory %s is not mounted", mountPath)
return "", fmt.Errorf("directory %s is not mounted", mountPath)
}
basemountPath := path.Join(pluginDir, MountsInGlobalPDPath)
for _, ref := range refs {
if strings.HasPrefix(ref, basemountPath) {
volumeID, err := filepath.Rel(basemountPath, ref)
if err != nil {
glog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
return "", err
}
return volumeID, nil
}
}
return path.Base(mountPath), nil
}
func listProcMounts(mountFilePath string) ([]MountPoint, error) {
content, err := utilio.ConsistentRead(mountFilePath, maxListTries)
if err != nil {
@ -353,9 +410,64 @@ func parseProcMounts(content []byte) ([]MountPoint, error) {
}
func (mounter *Mounter) MakeRShared(path string) error {
mountCmd := defaultMountCommand
mountArgs := []string{}
return doMakeRShared(path, procMountInfoPath, mountCmd, mountArgs)
return doMakeRShared(path, procMountInfoPath)
}
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
var pathType FileType
finfo, err := os.Stat(pathname)
if os.IsNotExist(err) {
return pathType, fmt.Errorf("path %q does not exist", pathname)
}
// err in call to os.Stat
if err != nil {
return pathType, err
}
mode := finfo.Sys().(*syscall.Stat_t).Mode
switch mode & syscall.S_IFMT {
case syscall.S_IFSOCK:
return FileTypeSocket, nil
case syscall.S_IFBLK:
return FileTypeBlockDev, nil
case syscall.S_IFCHR:
return FileTypeBlockDev, nil
case syscall.S_IFDIR:
return FileTypeDirectory, nil
case syscall.S_IFREG:
return FileTypeFile, nil
}
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}
func (mounter *Mounter) MakeDir(pathname string) error {
err := os.MkdirAll(pathname, os.FileMode(0755))
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
func (mounter *Mounter) MakeFile(pathname string) error {
f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644))
defer f.Close()
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
func (mounter *Mounter) ExistsPath(pathname string) bool {
_, err := os.Stat(pathname)
if err != nil {
return false
}
return true
}
// formatAndMount uses unix utils to format and mount the given disk
@ -424,7 +536,7 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string,
return mountErr
}
// diskLooksUnformatted uses 'lsblk' to see if the given disk is unformated
// getDiskFormat uses 'lsblk' to see if the given disk is unformated
func (mounter *SafeFormatAndMount) getDiskFormat(disk string) (string, error) {
args := []string{"-n", "-o", "FSTYPE", disk}
glog.V(4).Infof("Attempting to determine if disk %q is formatted using lsblk with args: (%v)", disk, args)
@ -526,7 +638,7 @@ func parseMountInfo(filename string) ([]mountInfo, error) {
// path is shared and bind-mounts it as rshared if needed. mountCmd and
// mountArgs are expected to contain mount-like command, doMakeRShared will add
// '--bind <path> <path>' and '--make-rshared <path>' to mountArgs.
func doMakeRShared(path string, mountInfoFilename string, mountCmd string, mountArgs []string) error {
func doMakeRShared(path string, mountInfoFilename string) error {
shared, err := isShared(path, mountInfoFilename)
if err != nil {
return err

View file

@ -18,6 +18,10 @@ limitations under the License.
package mount
import (
"errors"
)
type Mounter struct {
mounterPath string
}
@ -39,6 +43,12 @@ func (mounter *Mounter) Unmount(target string) error {
return nil
}
// GetMountRefs finds all other references to the device referenced
// by mountPath; returns a list of paths.
func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
return []string{}, nil
}
func (mounter *Mounter) List() ([]MountPoint, error) {
return []MountPoint{}, nil
}
@ -59,6 +69,10 @@ func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (str
return "", nil
}
func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
return "", nil
}
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
return false, nil
}
@ -78,3 +92,19 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string,
func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
return true, nil
}
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
return FileType("fake"), errors.New("not implemented")
}
func (mounter *Mounter) MakeDir(pathname string) error {
return nil
}
func (mounter *Mounter) MakeFile(pathname string) error {
return nil
}
func (mounter *Mounter) ExistsPath(pathname string) bool {
return true
}

View file

@ -22,9 +22,11 @@ import (
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"syscall"
"github.com/golang/glog"
)
@ -74,8 +76,24 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
return nil
}
// empty implementation for mounting azure file
return os.MkdirAll(target, 0755)
// currently only cifs mount is supported
if strings.ToLower(fstype) != "cifs" {
return fmt.Errorf("azureMount: only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, options)
}
cmdLine := fmt.Sprintf(`$User = "%s";$PWord = ConvertTo-SecureString -String "%s" -AsPlainText -Force;`+
`$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord`,
options[0], options[1])
bindSource = source
cmdLine += fmt.Sprintf(";New-SmbGlobalMapping -RemotePath %s -Credential $Credential", source)
if output, err := exec.Command("powershell", "/c", cmdLine).CombinedOutput(); err != nil {
// we don't return error here, even though New-SmbGlobalMapping failed, we still make it successful,
// will return error when Windows 2016 RS3 is ready on azure
glog.Errorf("azureMount: SmbGlobalMapping failed: %v, only SMB mount is supported now, output: %q", err, string(output))
return os.MkdirAll(target, 0755)
}
}
if output, err := exec.Command("cmd", "/c", "mklink", "/D", target, bindSource).CombinedOutput(); err != nil {
@ -97,6 +115,16 @@ func (mounter *Mounter) Unmount(target string) error {
return nil
}
// GetMountRefs finds all other references to the device(drive) referenced
// by mountPath; returns a list of paths.
func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
refs, err := getAllParentLinks(normalizeWindowsPath(mountPath))
if err != nil {
return nil, err
}
return refs, nil
}
// List returns a list of all mounted filesystems. todo
func (mounter *Mounter) List() ([]MountPoint, error) {
return []MountPoint{}, nil
@ -131,6 +159,33 @@ func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (str
return getDeviceNameFromMount(mounter, mountPath, pluginDir)
}
// getDeviceNameFromMount find the device(drive) name in which
// the mount path reference should match the given plugin directory. In case no mount path reference
// matches, returns the volume name taken from its given mountPath
func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
refs, err := GetMountRefs(mounter, mountPath)
if err != nil {
glog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
return "", err
}
if len(refs) == 0 {
return "", fmt.Errorf("directory %s is not mounted", mountPath)
}
basemountPath := normalizeWindowsPath(path.Join(pluginDir, MountsInGlobalPDPath))
for _, ref := range refs {
if strings.Contains(ref, basemountPath) {
volumeID, err := filepath.Rel(normalizeWindowsPath(basemountPath), ref)
if err != nil {
glog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
return "", err
}
return volumeID, nil
}
}
return path.Base(mountPath), nil
}
// DeviceOpened determines if the device is in use elsewhere
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
return false, nil
@ -147,6 +202,67 @@ func (mounter *Mounter) MakeRShared(path string) error {
return nil
}
// GetFileType checks for sockets/block/character devices
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
var pathType FileType
info, err := os.Stat(pathname)
if os.IsNotExist(err) {
return pathType, fmt.Errorf("path %q does not exist", pathname)
}
// err in call to os.Stat
if err != nil {
return pathType, err
}
mode := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
switch mode & syscall.S_IFMT {
case syscall.S_IFSOCK:
return FileTypeSocket, nil
case syscall.S_IFBLK:
return FileTypeBlockDev, nil
case syscall.S_IFCHR:
return FileTypeCharDev, nil
case syscall.S_IFDIR:
return FileTypeDirectory, nil
case syscall.S_IFREG:
return FileTypeFile, nil
}
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}
// MakeFile creates a new directory
func (mounter *Mounter) MakeDir(pathname string) error {
err := os.MkdirAll(pathname, os.FileMode(0755))
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
// MakeFile creates an empty file
func (mounter *Mounter) MakeFile(pathname string) error {
f, err := os.OpenFile(pathname, os.O_CREATE, os.FileMode(0644))
defer f.Close()
if err != nil {
if !os.IsExist(err) {
return err
}
}
return nil
}
// ExistsPath checks whether the path exists
func (mounter *Mounter) ExistsPath(pathname string) bool {
_, err := os.Stat(pathname)
if err != nil {
return false
}
return true
}
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
// Try to mount the disk
glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)
@ -204,3 +320,30 @@ func getDriveLetterByDiskNumber(diskNum string, exec Exec) (string, error) {
}
return string(output)[:1], nil
}
// getAllParentLinks walks all symbolic links and return all the parent targets recursively
func getAllParentLinks(path string) ([]string, error) {
const maxIter = 255
links := []string{}
for {
links = append(links, path)
if len(links) > maxIter {
return links, fmt.Errorf("unexpected length of parent links: %v", links)
}
fi, err := os.Lstat(path)
if err != nil {
return links, fmt.Errorf("Lstat: %v", err)
}
if fi.Mode()&os.ModeSymlink == 0 {
break
}
path, err = os.Readlink(path)
if err != nil {
return links, fmt.Errorf("Readlink error: %v", err)
}
}
return links, nil
}

View file

@ -25,78 +25,30 @@ import (
"strings"
"github.com/golang/glog"
"k8s.io/utils/exec"
"k8s.io/kubernetes/pkg/util/nsenter"
)
// NsenterMounter is part of experimental support for running the kubelet
// in a container. Currently, all docker containers receive their own mount
// namespaces. NsenterMounter works by executing nsenter to run commands in
const (
// hostProcMountsPath is the default mount path for rootfs
hostProcMountsPath = "/rootfs/proc/1/mounts"
// hostProcMountinfoPath is the default mount info path for rootfs
hostProcMountinfoPath = "/rootfs/proc/1/mountinfo"
)
// Currently, all docker containers receive their own mount namespaces.
// NsenterMounter works by executing nsenter to run commands in
// the host's mount namespace.
//
// NsenterMounter requires:
//
// 1. Docker >= 1.6 due to the dependency on the slave propagation mode
// of the bind-mount of the kubelet root directory in the container.
// Docker 1.5 used a private propagation mode for bind-mounts, so mounts
// performed in the host's mount namespace do not propagate out to the
// bind-mount in this docker version.
// 2. The host's root filesystem must be available at /rootfs
// 3. The nsenter binary must be on the Kubelet process' PATH in the container's
// filesystem.
// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at
// the present, this effectively means that the kubelet is running in a
// privileged container.
// 5. The volume path used by the Kubelet must be the same inside and outside
// the container and be writable by the container (to initialize volume)
// contents. TODO: remove this requirement.
// 6. The host image must have mount, findmnt, and umount binaries in /bin,
// /usr/sbin, or /usr/bin
// 7. The host image should have systemd-run in /bin, /usr/sbin, or /usr/bin
// For more information about mount propagation modes, see:
// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
type NsenterMounter struct {
// a map of commands to their paths on the host filesystem
paths map[string]string
ne *nsenter.Nsenter
}
func NewNsenterMounter() *NsenterMounter {
m := &NsenterMounter{
paths: map[string]string{
"mount": "",
"findmnt": "",
"umount": "",
"systemd-run": "",
},
}
// search for the mount command in other locations besides /usr/bin
for binary := range m.paths {
// default to root
m.paths[binary] = filepath.Join("/", binary)
for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} {
binPath := filepath.Join(path, binary)
if _, err := os.Stat(filepath.Join(hostRootFsPath, binPath)); err != nil {
continue
}
m.paths[binary] = binPath
break
}
// TODO: error, so that the kubelet can stop if the mounts don't exist
// (don't forget that systemd-run is optional)
}
return m
return &NsenterMounter{ne: nsenter.NewNsenter()}
}
// NsenterMounter implements mount.Interface
var _ = Interface(&NsenterMounter{})
const (
hostRootFsPath = "/rootfs"
hostProcMountsPath = "/rootfs/proc/1/mounts"
hostProcMountinfoPath = "/rootfs/proc/1/mountinfo"
hostMountNamespacePath = "/rootfs/proc/1/ns/mnt"
nsenterPath = "nsenter"
)
// Mount runs mount(8) in the host's root mount namespace. Aside from this
// aspect, Mount has the same semantics as the mounter returned by mount.New()
func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
@ -116,26 +68,22 @@ func (n *NsenterMounter) Mount(source string, target string, fstype string, opti
// doNsenterMount nsenters the host's mount namespace and performs the
// requested mount.
func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error {
glog.V(5).Infof("nsenter Mounting %s %s %s %v", source, target, fstype, options)
args := n.makeNsenterArgs(source, target, fstype, options)
glog.V(5).Infof("Mount command: %v %v", nsenterPath, args)
exec := exec.New()
outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
glog.V(5).Infof("nsenter mount %s %s %s %v", source, target, fstype, options)
cmd, args := n.makeNsenterArgs(source, target, fstype, options)
outputBytes, err := n.ne.Exec(cmd, args).CombinedOutput()
if len(outputBytes) != 0 {
glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes))
}
return err
}
// makeNsenterArgs makes a list of argument to nsenter in order to do the
// requested mount.
func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) []string {
mountCmd := n.absHostPath("mount")
func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) (string, []string) {
mountCmd := n.ne.AbsHostPath("mount")
mountArgs := makeMountArgs(source, target, fstype, options)
if systemdRunPath, hasSystemd := n.paths["systemd-run"]; hasSystemd {
if systemdRunPath, hasSystemd := n.ne.SupportsSystemd(); hasSystemd {
// Complete command line:
// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/systemd-run --description=... --scope -- /bin/mount -t <type> <what> <where>
// Expected flow is:
@ -165,34 +113,20 @@ func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options
// No code here, mountCmd and mountArgs use /bin/mount
}
nsenterArgs := []string{
"--mount=" + hostMountNamespacePath,
"--",
mountCmd,
}
nsenterArgs = append(nsenterArgs, mountArgs...)
return nsenterArgs
return mountCmd, mountArgs
}
// Unmount runs umount(8) in the host's mount namespace.
func (n *NsenterMounter) Unmount(target string) error {
args := []string{
"--mount=" + hostMountNamespacePath,
"--",
n.absHostPath("umount"),
target,
}
args := []string{target}
// No need to execute systemd-run here, it's enough that unmount is executed
// in the host's mount namespace. It will finish appropriate fuse daemon(s)
// running in any scope.
glog.V(5).Infof("Unmount command: %v %v", nsenterPath, args)
exec := exec.New()
outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
glog.V(5).Infof("nsenter unmount args: %v", args)
outputBytes, err := n.ne.Exec("umount", args).CombinedOutput()
if len(outputBytes) != 0 {
glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
}
return err
}
@ -207,7 +141,7 @@ func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
return ((mp.Path == dir) || (mp.Path == deletedDir))
return (mp.Path == dir) || (mp.Path == deletedDir)
}
// IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt
@ -227,11 +161,9 @@ func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
// the first of multiple possible mountpoints using --first-only.
// Also add fstype output to make sure that the output of target file will give the full path
// TODO: Need more refactoring for this function. Track the solution with issue #26996
args := []string{"--mount=" + hostMountNamespacePath, "--", n.absHostPath("findmnt"), "-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
glog.V(5).Infof("findmnt command: %v %v", nsenterPath, args)
exec := exec.New()
out, err := exec.Command(nsenterPath, args...).CombinedOutput()
args := []string{"-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
glog.V(5).Infof("nsenter findmnt args: %v", args)
out, err := n.ne.Exec("findmnt", args).CombinedOutput()
if err != nil {
glog.V(2).Infof("Failed findmnt command for path %s: %v", file, err)
// Different operating systems behave differently for paths which are not mount points.
@ -277,7 +209,9 @@ func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) {
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) {
return pathIsDevice(pathname)
pathType, err := n.GetFileType(pathname)
isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
return isDevice, err
}
//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
@ -285,20 +219,54 @@ func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (st
return getDeviceNameFromMount(n, mountPath, pluginDir)
}
func (n *NsenterMounter) absHostPath(command string) string {
path, ok := n.paths[command]
if !ok {
return command
}
return path
func (n *NsenterMounter) MakeRShared(path string) error {
return doMakeRShared(path, hostProcMountinfoPath)
}
func (n *NsenterMounter) MakeRShared(path string) error {
nsenterCmd := nsenterPath
nsenterArgs := []string{
"--mount=" + hostMountNamespacePath,
"--",
n.absHostPath("mount"),
func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) {
var pathType FileType
outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput()
if err != nil {
return pathType, err
}
return doMakeRShared(path, hostProcMountinfoPath, nsenterCmd, nsenterArgs)
switch string(outputBytes) {
case "socket":
return FileTypeSocket, nil
case "character special file":
return FileTypeCharDev, nil
case "block special file":
return FileTypeBlockDev, nil
case "directory":
return FileTypeDirectory, nil
case "regular file":
return FileTypeFile, nil
}
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}
func (mounter *NsenterMounter) MakeDir(pathname string) error {
args := []string{"-p", pathname}
if _, err := mounter.ne.Exec("mkdir", args).CombinedOutput(); err != nil {
return err
}
return nil
}
func (mounter *NsenterMounter) MakeFile(pathname string) error {
args := []string{pathname}
if _, err := mounter.ne.Exec("touch", args).CombinedOutput(); err != nil {
return err
}
return nil
}
func (mounter *NsenterMounter) ExistsPath(pathname string) bool {
args := []string{pathname}
_, err := mounter.ne.Exec("ls", args).CombinedOutput()
if err == nil {
return true
}
return false
}

View file

@ -18,6 +18,10 @@ limitations under the License.
package mount
import (
"errors"
)
type NsenterMounter struct{}
func NewNsenterMounter() *NsenterMounter {
@ -65,3 +69,19 @@ func (*NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (stri
func (*NsenterMounter) MakeRShared(path string) error {
return nil
}
func (*NsenterMounter) GetFileType(_ string) (FileType, error) {
return FileType("fake"), errors.New("not implemented")
}
func (*NsenterMounter) MakeDir(pathname string) error {
return nil
}
func (*NsenterMounter) MakeFile(pathname string) error {
return nil
}
func (*NsenterMounter) ExistsPath(pathname string) bool {
return true
}

124
vendor/k8s.io/kubernetes/pkg/util/nsenter/nsenter.go generated vendored Normal file
View file

@ -0,0 +1,124 @@
// +build linux
/*
Copyright 2017 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 nsenter
import (
"fmt"
"os"
"path/filepath"
"k8s.io/utils/exec"
"github.com/golang/glog"
)
const (
hostRootFsPath = "/rootfs"
// hostProcMountNsPath is the default mount namespace for rootfs
hostProcMountNsPath = "/rootfs/proc/1/ns/mnt"
// nsenterPath is the default nsenter command
nsenterPath = "nsenter"
)
// Nsenter is part of experimental support for running the kubelet
// in a container.
//
// Nsenter requires:
//
// 1. Docker >= 1.6 due to the dependency on the slave propagation mode
// of the bind-mount of the kubelet root directory in the container.
// Docker 1.5 used a private propagation mode for bind-mounts, so mounts
// performed in the host's mount namespace do not propagate out to the
// bind-mount in this docker version.
// 2. The host's root filesystem must be available at /rootfs
// 3. The nsenter binary must be on the Kubelet process' PATH in the container's
// filesystem.
// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at
// the present, this effectively means that the kubelet is running in a
// privileged container.
// 5. The volume path used by the Kubelet must be the same inside and outside
// the container and be writable by the container (to initialize volume)
// contents. TODO: remove this requirement.
// 6. The host image must have "mount", "findmnt", "umount", "stat", "touch",
// "mkdir", "ls", "sh" and "chmod" binaries in /bin, /usr/sbin, or /usr/bin
// 7. The host image should have systemd-run in /bin, /usr/sbin, or /usr/bin
// For more information about mount propagation modes, see:
// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
type Nsenter struct {
// a map of commands to their paths on the host filesystem
paths map[string]string
}
// NewNsenter constructs a new instance of Nsenter
func NewNsenter() *Nsenter {
ne := &Nsenter{
paths: map[string]string{
"mount": "",
"findmnt": "",
"umount": "",
"systemd-run": "",
"stat": "",
"touch": "",
"mkdir": "",
"ls": "",
"sh": "",
"chmod": "",
},
}
// search for the required commands in other locations besides /usr/bin
for binary := range ne.paths {
// default to root
ne.paths[binary] = filepath.Join("/", binary)
for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} {
binPath := filepath.Join(path, binary)
if _, err := os.Stat(filepath.Join(hostRootFsPath, binPath)); err != nil {
continue
}
ne.paths[binary] = binPath
break
}
// TODO: error, so that the kubelet can stop if the paths don't exist
// (don't forget that systemd-run is optional)
}
return ne
}
// Exec executes nsenter commands in hostProcMountNsPath mount namespace
func (ne *Nsenter) Exec(cmd string, args []string) exec.Cmd {
fullArgs := append([]string{fmt.Sprintf("--mount=%s", hostProcMountNsPath), "--"},
append([]string{ne.AbsHostPath(cmd)}, args...)...)
glog.V(5).Infof("Running nsenter command: %v %v", nsenterPath, fullArgs)
exec := exec.New()
return exec.Command(nsenterPath, fullArgs...)
}
// AbsHostPath returns the absolute runnable path for a specified command
func (ne *Nsenter) AbsHostPath(command string) string {
path, ok := ne.paths[command]
if !ok {
return command
}
return path
}
// SupportsSystemd checks whether command systemd-run exists
func (ne *Nsenter) SupportsSystemd() (string, bool) {
systemdRunPath, hasSystemd := ne.paths["systemd-run"]
return systemdRunPath, hasSystemd
}

View file

@ -0,0 +1,50 @@
// +build !linux
/*
Copyright 2017 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 nsenter
import (
"k8s.io/utils/exec"
)
// Nsenter is part of experimental support for running the kubelet
// in a container.
type Nsenter struct {
// a map of commands to their paths on the host filesystem
Paths map[string]string
}
// NewNsenter constructs a new instance of Nsenter
func NewNsenter() *Nsenter {
return &Nsenter{}
}
// Exec executes nsenter commands in hostProcMountNsPath mount namespace
func (ne *Nsenter) Exec(args ...string) exec.Cmd {
return nil
}
// AbsHostPath returns the absolute runnable path for a specified command
func (ne *Nsenter) AbsHostPath(command string) string {
return ""
}
// SupportsSystemd checks whether command systemd-run exists
func (ne *Nsenter) SupportsSystemd() (string, bool) {
return "", false
}

View file

@ -35,7 +35,7 @@ const (
UNTAINTED = "untainted"
)
// parseTaint parses a taint from a string. Taint must be off the format '<key>=<value>:<effect>'.
// parseTaint parses a taint from a string. Taint must be of the format '<key>=<value>:<effect>'.
func parseTaint(st string) (v1.Taint, error) {
var taint v1.Taint
parts := strings.Split(st, "=")
@ -45,15 +45,14 @@ func parseTaint(st string) (v1.Taint, error) {
parts2 := strings.Split(parts[1], ":")
effect := v1.TaintEffect(parts2[1])
errs := validation.IsValidLabelValue(parts2[0])
if len(parts2) != 2 || len(errs) != 0 {
return taint, fmt.Errorf("invalid taint spec: %v, %s", st, strings.Join(errs, "; "))
}
if effect != v1.TaintEffectNoSchedule && effect != v1.TaintEffectPreferNoSchedule && effect != v1.TaintEffectNoExecute {
return taint, fmt.Errorf("invalid taint spec: %v, unsupported taint effect", st)
effect := v1.TaintEffect(parts2[1])
if err := validateTaintEffect(effect); err != nil {
return taint, err
}
taint.Key = parts[0]
@ -63,6 +62,14 @@ func parseTaint(st string) (v1.Taint, error) {
return taint, nil
}
func validateTaintEffect(effect v1.TaintEffect) error {
if effect != v1.TaintEffectNoSchedule && effect != v1.TaintEffectPreferNoSchedule && effect != v1.TaintEffectNoExecute {
return fmt.Errorf("invalid taint effect: %v, unsupported taint effect", effect)
}
return nil
}
// NewTaintsVar wraps []api.Taint in a struct that implements flag.Value to allow taints to be
// bound to command line flags.
func NewTaintsVar(ptr *[]api.Taint) taintsVar {
@ -76,6 +83,10 @@ type taintsVar struct {
}
func (t taintsVar) Set(s string) error {
if len(s) == 0 {
*t.ptr = nil
return nil
}
sts := strings.Split(s, ",")
var taints []api.Taint
for _, st := range sts {
@ -91,7 +102,7 @@ func (t taintsVar) Set(s string) error {
func (t taintsVar) String() string {
if len(*t.ptr) == 0 {
return "<nil>"
return ""
}
var taints []string
for _, taint := range *t.ptr {
@ -134,6 +145,14 @@ func ParseTaints(spec []string) ([]v1.Taint, []v1.Taint, error) {
taintKey = parts[0]
effect = v1.TaintEffect(parts[1])
}
// If effect is specified, need to validate it.
if len(effect) > 0 {
err := validateTaintEffect(effect)
if err != nil {
return nil, nil, err
}
}
taintsToRemove = append(taintsToRemove, v1.Taint{Key: taintKey, Effect: effect})
} else {
return nil, nil, fmt.Errorf("unknown taint spec: %v", taintSpec)
@ -238,11 +257,7 @@ func DeleteTaint(taints []v1.Taint, taintToDelete *v1.Taint) ([]v1.Taint, bool)
// RemoveTaint tries to remove a taint from annotations list. Returns a new copy of updated Node and true if something was updated
// false otherwise.
func RemoveTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) {
objCopy, err := api.Scheme.DeepCopy(node)
if err != nil {
return nil, false, err
}
newNode := objCopy.(*v1.Node)
newNode := node.DeepCopy()
nodeTaints := newNode.Spec.Taints
if len(nodeTaints) == 0 {
return newNode, false, nil
@ -260,11 +275,7 @@ func RemoveTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) {
// AddOrUpdateTaint tries to add a taint to annotations list. Returns a new copy of updated Node and true if something was updated
// false otherwise.
func AddOrUpdateTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) {
objCopy, err := api.Scheme.DeepCopy(node)
if err != nil {
return nil, false, err
}
newNode := objCopy.(*v1.Node)
newNode := node.DeepCopy()
nodeTaints := newNode.Spec.Taints
var newTaints []v1.Taint

View file

@ -181,12 +181,11 @@ func (v *Version) String() string {
// compareInternal returns -1 if v is less than other, 1 if it is greater than other, or 0
// if they are equal
func (v *Version) compareInternal(other *Version) int {
for i := range v.components {
vLen := len(v.components)
oLen := len(other.components)
for i := 0; i < vLen && i < oLen; i++ {
switch {
case i >= len(other.components):
if v.components[i] != 0 {
return 1
}
case other.components[i] < v.components[i]:
return 1
case other.components[i] > v.components[i]:
@ -194,6 +193,14 @@ func (v *Version) compareInternal(other *Version) int {
}
}
// If components are common but one has more items and they are not zeros, it is bigger
switch {
case oLen < vLen && !onlyZeros(v.components[oLen:]):
return 1
case oLen > vLen && !onlyZeros(other.components[vLen:]):
return -1
}
if !v.semver || !other.semver {
return 0
}
@ -209,10 +216,7 @@ func (v *Version) compareInternal(other *Version) int {
vPR := strings.Split(v.preRelease, ".")
oPR := strings.Split(other.preRelease, ".")
for i := range vPR {
if i >= len(oPR) {
return 1
}
for i := 0; i < len(vPR) && i < len(oPR); i++ {
vNum, err := strconv.ParseUint(vPR[i], 10, 0)
if err == nil {
oNum, err := strconv.ParseUint(oPR[i], 10, 0)
@ -234,9 +238,26 @@ func (v *Version) compareInternal(other *Version) int {
}
}
switch {
case len(oPR) < len(vPR):
return 1
case len(oPR) > len(vPR):
return -1
}
return 0
}
// returns false if array contain any non-zero element
func onlyZeros(array []uint) bool {
for _, num := range array {
if num != 0 {
return false
}
}
return true
}
// AtLeast tests if a version is at least equal to a given minimum version. If both
// Versions are Semantic Versions, this will use the Semantic Version comparison
// algorithm. Otherwise, it will compare only the numeric components, with non-present

View file

@ -69,8 +69,6 @@ type VolumeOptions struct {
CloudTags *map[string]string
// Volume provisioning parameters from StorageClass
Parameters map[string]string
// This flag helps identify whether kubelet is running in a container
Containerized bool
}
type DynamicPluginProber interface {
@ -713,7 +711,7 @@ func NewPersistentVolumeRecyclerPodTemplate() *v1.Pod {
Containers: []v1.Container{
{
Name: "pv-recycler",
Image: "gcr.io/google_containers/busybox",
Image: "busybox:1.27",
Command: []string{"/bin/sh"},
Args: []string{"-c", "test -e /scrub && rm -rf /scrub/..?* /scrub/.[!.]* /scrub/* && test -z \"$(ls -A /scrub)\" || exit 1"},
VolumeMounts: []v1.VolumeMount{

View file

@ -36,7 +36,6 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
volutil "k8s.io/kubernetes/pkg/volume/util"
)
type RecycleEventRecorder func(eventtype, message string)
@ -48,8 +47,8 @@ type RecycleEventRecorder func(eventtype, message string)
// attempted before returning.
//
// In case there is a pod with the same namespace+name already running, this
// function assumes it's an older instance of the recycler pod and watches
// this old pod instead of starting a new one.
// function deletes it as it is not able to judge if it is an old recycler
// or user has forged a fake recycler to block Kubernetes from recycling.//
//
// pod - the pod designed by a volume plugin to recycle the volume. pod.Name
// will be overwritten with unique name based on PV.Name.
@ -81,20 +80,44 @@ func internalRecycleVolumeByWatchingPodUntilCompletion(pvName string, pod *v1.Po
_, err = recyclerClient.CreatePod(pod)
if err != nil {
if errors.IsAlreadyExists(err) {
glog.V(5).Infof("old recycler pod %q found for volume", pod.Name)
deleteErr := recyclerClient.DeletePod(pod.Name, pod.Namespace)
if deleteErr != nil {
return fmt.Errorf("failed to delete old recycler pod %s/%s: %s", pod.Namespace, pod.Name, deleteErr)
}
// Recycler will try again and the old pod will be hopefuly deleted
// at that time.
return fmt.Errorf("old recycler pod found, will retry later")
} else {
return fmt.Errorf("unexpected error creating recycler pod: %+v\n", err)
}
}
defer func(pod *v1.Pod) {
glog.V(2).Infof("deleting recycler pod %s/%s", pod.Namespace, pod.Name)
if err := recyclerClient.DeletePod(pod.Name, pod.Namespace); err != nil {
glog.Errorf("failed to delete recycler pod %s/%s: %v", pod.Namespace, pod.Name, err)
}
}(pod)
err = waitForPod(pod, recyclerClient, podCh)
// Now only the old pod or the new pod run. Watch it until it finishes
// and send all events on the pod to the PV
// In all cases delete the recycler pod and log its result.
glog.V(2).Infof("deleting recycler pod %s/%s", pod.Namespace, pod.Name)
deleteErr := recyclerClient.DeletePod(pod.Name, pod.Namespace)
if deleteErr != nil {
glog.Errorf("failed to delete recycler pod %s/%s: %v", pod.Namespace, pod.Name, err)
}
// Returning recycler error is preferred, the pod will be deleted again on
// the next retry.
if err != nil {
return fmt.Errorf("failed to recycle volume: %s", err)
}
// Recycle succeeded but we failed to delete the recycler pod. Report it,
// the controller will re-try recycling the PV again shortly.
if deleteErr != nil {
return fmt.Errorf("failed to delete recycler pod: %s", deleteErr)
}
return nil
}
// waitForPod watches the pod it until it finishes and send all events on the
// pod to the PV.
func waitForPod(pod *v1.Pod, recyclerClient recyclerClient, podCh <-chan watch.Event) error {
for {
event, ok := <-podCh
if !ok {
@ -164,15 +187,15 @@ type realRecyclerClient struct {
}
func (c *realRecyclerClient) CreatePod(pod *v1.Pod) (*v1.Pod, error) {
return c.client.Core().Pods(pod.Namespace).Create(pod)
return c.client.CoreV1().Pods(pod.Namespace).Create(pod)
}
func (c *realRecyclerClient) GetPod(name, namespace string) (*v1.Pod, error) {
return c.client.Core().Pods(namespace).Get(name, metav1.GetOptions{})
return c.client.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
}
func (c *realRecyclerClient) DeletePod(name, namespace string) error {
return c.client.Core().Pods(namespace).Delete(name, nil)
return c.client.CoreV1().Pods(namespace).Delete(name, nil)
}
func (c *realRecyclerClient) Event(eventtype, message string) {
@ -189,13 +212,13 @@ func (c *realRecyclerClient) WatchPod(name, namespace string, stopChannel chan s
Watch: true,
}
podWatch, err := c.client.Core().Pods(namespace).Watch(options)
podWatch, err := c.client.CoreV1().Pods(namespace).Watch(options)
if err != nil {
return nil, err
}
eventSelector, _ := fields.ParseSelector("involvedObject.name=" + name)
eventWatch, err := c.client.Core().Events(namespace).Watch(metav1.ListOptions{
eventWatch, err := c.client.CoreV1().Events(namespace).Watch(metav1.ListOptions{
FieldSelector: eventSelector.String(),
Watch: true,
})
@ -400,13 +423,6 @@ func getPVCNameHashAndIndexOffset(pvcName string) (hash uint32, index uint32) {
func UnmountViaEmptyDir(dir string, host VolumeHost, volName string, volSpec Spec, podUID types.UID) error {
glog.V(3).Infof("Tearing down volume %v for pod %v at %v", volName, podUID, dir)
if pathExists, pathErr := volutil.PathExists(dir); pathErr != nil {
return fmt.Errorf("Error checking if path exists: %v", pathErr)
} else if !pathExists {
glog.Warningf("Warning: Unmount skipped because path does not exist: %v", dir)
return nil
}
// Wrap EmptyDir, let it do the teardown.
wrapped, err := host.NewWrapperUnmounter(volName, volSpec, podUID)
if err != nil {

View file

@ -24,8 +24,9 @@ import (
var storageOperationMetric = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "storage_operation_duration_seconds",
Help: "Storage operation duration",
Name: "storage_operation_duration_seconds",
Help: "Storage operation duration",
Buckets: []float64{.1, .25, .5, 1, 2.5, 5, 10, 15, 25, 50},
},
[]string{"volume_plugin", "operation_name"},
)

View file

@ -32,7 +32,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/legacyscheme"
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
"k8s.io/kubernetes/pkg/util/mount"
@ -147,7 +147,7 @@ func GetSecretForPod(pod *v1.Pod, secretName string, kubeClient clientset.Interf
if kubeClient == nil {
return secret, fmt.Errorf("Cannot get kube client")
}
secrets, err := kubeClient.Core().Secrets(pod.Namespace).Get(secretName, metav1.GetOptions{})
secrets, err := kubeClient.CoreV1().Secrets(pod.Namespace).Get(secretName, metav1.GetOptions{})
if err != nil {
return secret, err
}
@ -163,7 +163,7 @@ func GetSecretForPV(secretNamespace, secretName, volumePluginName string, kubeCl
if kubeClient == nil {
return secret, fmt.Errorf("Cannot get kube client")
}
secrets, err := kubeClient.Core().Secrets(secretNamespace).Get(secretName, metav1.GetOptions{})
secrets, err := kubeClient.CoreV1().Secrets(secretNamespace).Get(secretName, metav1.GetOptions{})
if err != nil {
return secret, err
}
@ -233,7 +233,7 @@ func LoadPodFromFile(filePath string) (*v1.Pod, error) {
}
pod := &v1.Pod{}
codec := api.Codecs.LegacyCodec(api.Registry.GroupOrDie(v1.GroupName).GroupVersion)
codec := legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion)
if err := runtime.DecodeInto(codec, podDef, pod); err != nil {
return nil, fmt.Errorf("failed decoding file: %v", err)
}