Merge pull request #34789 from AdoHe/external_service

Automatic merge from submit-queue

create service add create ExternalName service implementation

@kubernetes/kubectl create service add ExternalName support, refer #34731 for more detail.

```release-note
kubectl create service externalname
```
This commit is contained in:
Kubernetes Submit Queue 2016-12-02 07:29:06 -08:00 committed by GitHub
commit bbd447f555
8 changed files with 168 additions and 9 deletions

View File

@ -46,6 +46,7 @@ docs/man/man1/kubectl-create-secret-generic.1
docs/man/man1/kubectl-create-secret-tls.1
docs/man/man1/kubectl-create-secret.1
docs/man/man1/kubectl-create-service-clusterip.1
docs/man/man1/kubectl-create-service-externalname.1
docs/man/man1/kubectl-create-service-loadbalancer.1
docs/man/man1/kubectl-create-service-nodeport.1
docs/man/man1/kubectl-create-service.1
@ -126,6 +127,7 @@ docs/user-guide/kubectl/kubectl_create_secret_generic.md
docs/user-guide/kubectl/kubectl_create_secret_tls.md
docs/user-guide/kubectl/kubectl_create_service.md
docs/user-guide/kubectl/kubectl_create_service_clusterip.md
docs/user-guide/kubectl/kubectl_create_service_externalname.md
docs/user-guide/kubectl/kubectl_create_service_loadbalancer.md
docs/user-guide/kubectl/kubectl_create_service_nodeport.md
docs/user-guide/kubectl/kubectl_create_serviceaccount.md

View File

@ -0,0 +1,3 @@
This file is autogenerated, but we've stopped checking such files into the
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
populate this file.

View File

@ -0,0 +1,36 @@
<!-- BEGIN MUNGE: UNVERSIONED_WARNING -->
<!-- BEGIN STRIP_FOR_RELEASE -->
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<h2>PLEASE NOTE: This document applies to the HEAD of the source tree</h2>
If you are using a released version of Kubernetes, you should
refer to the docs that go with that version.
Documentation for other releases can be found at
[releases.k8s.io](http://releases.k8s.io).
</strong>
--
<!-- END STRIP_FOR_RELEASE -->
<!-- END MUNGE: UNVERSIONED_WARNING -->
This file is autogenerated, but we've stopped checking such files into the
repository to reduce the need for rebases. Please run hack/generate-docs.sh to
populate this file.
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_create_service_externalname.md?pixel)]()
<!-- END MUNGE: GENERATED_ANALYTICS -->

View File

@ -1996,6 +1996,22 @@ __EOF__
# Post-condition: Only the default kubernetes services exist
kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:'
### Create an ExternalName service
# Pre-condition: Only the default kubernetes service exist
kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:'
# Command
kubectl create service externalname beep-boop --external-name bar.com
# Post-condition: beep-boop service is created
kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'beep-boop:kubernetes:'
### Delete beep-boop service by id
# Pre-condition: beep-boop service exists
kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'beep-boop:kubernetes:'
# Command
kubectl delete service beep-boop "${kube_flags[@]}"
# Post-condition: Only the default kubernetes services exist
kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:'
###########################
# Replication controllers #

View File

@ -208,6 +208,7 @@ external-etcd-endpoints
external-etcd-keyfile
external-hostname
external-ip
external-name
extra-peer-dirs
experimental-fail-swap-on
failover-timeout

View File

@ -40,6 +40,7 @@ func NewCmdCreateService(f cmdutil.Factory, cmdOut, errOut io.Writer) *cobra.Com
cmd.AddCommand(NewCmdCreateServiceClusterIP(f, cmdOut))
cmd.AddCommand(NewCmdCreateServiceNodePort(f, cmdOut))
cmd.AddCommand(NewCmdCreateServiceLoadBalancer(f, cmdOut))
cmd.AddCommand(NewCmdCreateServiceExternalName(f, cmdOut))
return cmd
}
@ -218,3 +219,64 @@ func CreateServiceLoadBalancer(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.C
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}
var (
serviceExternalNameLong = templates.LongDesc(`
Create an ExternalName service with the specified name.
ExternalName service references to an external DNS address instead of
only pods, which will allow application authors to reference services
that exist off platform, on other clusters, or locally.`)
serviceExternalNameExample = templates.Examples(`
# Create a new ExternalName service named my-ns
kubectl create service externalname my-ns --external-name bar.com`)
)
// NewCmdCreateServiceExternalName is a macro command for creating a ExternalName service
func NewCmdCreateServiceExternalName(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "externalname NAME --external-name external.name [--dry-run]",
Short: "Create an ExternalName service.",
Long: serviceExternalNameLong,
Example: serviceExternalNameExample,
Run: func(cmd *cobra.Command, args []string) {
err := CreateExternalNameService(f, cmdOut, cmd, args)
cmdutil.CheckErr(err)
},
}
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
cmdutil.AddGeneratorFlags(cmd, cmdutil.ServiceExternalNameGeneratorV1Name)
addPortFlags(cmd)
cmd.Flags().String("external-name", "", "external name of service")
cmd.MarkFlagRequired("external-name")
return cmd
}
// CreateExternalNameService is the implementation of the service externalname command
func CreateExternalNameService(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
var generator kubectl.StructuredGenerator
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
case cmdutil.ServiceExternalNameGeneratorV1Name:
generator = &kubectl.ServiceCommonGeneratorV1{
Name: name,
Type: api.ServiceTypeExternalName,
ExternalName: cmdutil.GetFlagString(cmd, "external-name"),
ClusterIP: "",
}
default:
return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName))
}
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}

View File

@ -193,6 +193,7 @@ const (
ServiceNodePortGeneratorV1Name = "service-nodeport/v1"
ServiceClusterIPGeneratorV1Name = "service-clusterip/v1"
ServiceLoadBalancerGeneratorV1Name = "service-loadbalancer/v1"
ServiceExternalNameGeneratorV1Name = "service-externalname/v1"
ServiceAccountV1GeneratorName = "serviceaccount/v1"
HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1"
HorizontalPodAutoscalerV1GeneratorName = "horizontalpodautoscaler/v1"

View File

@ -24,14 +24,16 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/validation"
)
type ServiceCommonGeneratorV1 struct {
Name string
TCP []string
Type api.ServiceType
ClusterIP string
NodePort int
Name string
TCP []string
Type api.ServiceType
ClusterIP string
NodePort int
ExternalName string
}
type ServiceClusterIPGeneratorV1 struct {
@ -46,6 +48,11 @@ type ServiceLoadBalancerGeneratorV1 struct {
ServiceCommonGeneratorV1
}
// TODO: is this really necessary?
type ServiceExternalNameGeneratorV1 struct {
ServiceCommonGeneratorV1
}
func (ServiceClusterIPGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
@ -67,6 +74,13 @@ func (ServiceLoadBalancerGeneratorV1) ParamNames() []GeneratorParam {
}
}
func (ServiceExternalNameGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
{"externalname", true},
}
}
func parsePorts(portString string) (int32, intstr.IntOrString, error) {
portStringSlice := strings.Split(portString, ":")
@ -100,9 +114,14 @@ func (s ServiceCommonGeneratorV1) GenerateCommon(params map[string]interface{})
if !isString {
return fmt.Errorf("expected string, saw %v for 'clusterip'", clusterip)
}
externalname, isString := params["externalname"].(string)
if !isString {
return fmt.Errorf("expected string, saw %v for 'externalname'", externalname)
}
s.Name = name
s.TCP = tcpStrings
s.ClusterIP = clusterip
s.ExternalName = externalname
return nil
}
@ -145,6 +164,19 @@ func (s ServiceClusterIPGeneratorV1) Generate(params map[string]interface{}) (ru
return delegate.StructuredGenerate()
}
func (s ServiceExternalNameGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
err := ValidateParams(s.ParamNames(), params)
if err != nil {
return nil, err
}
delegate := &ServiceCommonGeneratorV1{Type: api.ServiceTypeExternalName, ClusterIP: ""}
err = delegate.GenerateCommon(params)
if err != nil {
return nil, err
}
return delegate.StructuredGenerate()
}
// validate validates required fields are set to support structured generation
func (s ServiceCommonGeneratorV1) validate() error {
if len(s.Name) == 0 {
@ -159,9 +191,14 @@ func (s ServiceCommonGeneratorV1) validate() error {
if s.ClusterIP == api.ClusterIPNone && len(s.TCP) > 0 {
return fmt.Errorf("can not map ports with clusterip=None")
}
if s.ClusterIP != api.ClusterIPNone && len(s.TCP) == 0 {
if s.ClusterIP != api.ClusterIPNone && len(s.TCP) == 0 && s.Type != api.ServiceTypeExternalName {
return fmt.Errorf("at least one tcp port specifier must be provided")
}
if s.Type == api.ServiceTypeExternalName {
if errs := validation.IsDNS1123Subdomain(s.ExternalName); len(errs) != 0 {
return fmt.Errorf("invalid service external name %s", s.ExternalName)
}
}
return nil
}
@ -199,9 +236,10 @@ func (s ServiceCommonGeneratorV1) StructuredGenerate() (runtime.Object, error) {
Labels: labels,
},
Spec: api.ServiceSpec{
Type: api.ServiceType(s.Type),
Selector: selector,
Ports: ports,
Type: api.ServiceType(s.Type),
Selector: selector,
Ports: ports,
ExternalName: s.ExternalName,
},
}
if len(s.ClusterIP) > 0 {