mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 22:46:12 +00:00
Allow kubectl expose
to be polymorphic to the source of the selector
Allows exposing new resource types to be exposed (OpenShift deployment controllers, copying services, and other resources that will have pod label selectors). Also fixed a bug where the selector wasn't exposed because of parameter defaulting.
This commit is contained in:
parent
a34f39aee4
commit
64f91f7dac
@ -7,11 +7,11 @@ Take a replicated application and expose it as Kubernetes Service
|
||||
|
||||
Take a replicated application and expose it as Kubernetes Service.
|
||||
|
||||
Looks up a ReplicationController by name, and uses the selector for that replication controller
|
||||
as the selector for a new Service on the specified port.
|
||||
Looks up a replication controller or service by name and uses the selector for that resource as the
|
||||
selector for a new Service on the specified port.
|
||||
|
||||
```
|
||||
kubectl expose NAME --port=port [--protocol=TCP|UDP] [--container-port=number-or-name] [--service-name=name] [--public-ip=ip] [--create-external-load-balancer=bool]
|
||||
kubectl expose RESOURCE NAME --port=port [--protocol=TCP|UDP] [--container-port=number-or-name] [--service-name=name] [--public-ip=ip] [--create-external-load-balancer=bool]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@ -20,6 +20,9 @@ kubectl expose NAME --port=port [--protocol=TCP|UDP] [--container-port=number-or
|
||||
// Creates a service for a replicated nginx, which serves on port 80 and connects to the containers on port 8000.
|
||||
$ kubectl expose nginx --port=80 --container-port=8000
|
||||
|
||||
// Creates a second service based on the above service, exposing the container port 8443 as port 443 with the name "nginx-https"
|
||||
$ kubectl expose service nginx --port=443 --container-port=8443 --service-name=nginx-https
|
||||
|
||||
// Create a service for a replicated streaming application on port 4100 balancing UDP traffic and named 'video-stream'.
|
||||
$ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream
|
||||
```
|
||||
|
@ -16,8 +16,8 @@ kubectl expose \- Take a replicated application and expose it as Kubernetes Serv
|
||||
Take a replicated application and expose it as Kubernetes Service.
|
||||
|
||||
.PP
|
||||
Looks up a ReplicationController by name, and uses the selector for that replication controller
|
||||
as the selector for a new Service on the specified port.
|
||||
Looks up a replication controller or service by name and uses the selector for that resource as the
|
||||
selector for a new Service on the specified port.
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
@ -197,6 +197,9 @@ as the selector for a new Service on the specified port.
|
||||
// Creates a service for a replicated nginx, which serves on port 80 and connects to the containers on port 8000.
|
||||
$ kubectl expose nginx \-\-port=80 \-\-container\-port=8000
|
||||
|
||||
// Creates a second service based on the above service, exposing the container port 8443 as port 443 with the name "nginx\-https"
|
||||
$ kubectl expose service nginx \-\-port=443 \-\-container\-port=8443 \-\-service\-name=nginx\-https
|
||||
|
||||
// Create a service for a replicated streaming application on port 4100 balancing UDP traffic and named 'video\-stream'.
|
||||
$ kubectl expose streamer \-\-port=4100 \-\-protocol=udp \-\-service\-name=video\-stream
|
||||
|
||||
|
@ -131,11 +131,13 @@ for version in "${kube_api_versions[@]}"; do
|
||||
labels_field="labels"
|
||||
service_selector_field="selector"
|
||||
rc_replicas_field="desiredState.replicas"
|
||||
port_field="port"
|
||||
if [ "$version" = "v1beta3" ]; then
|
||||
id_field="metadata.name"
|
||||
labels_field="metadata.labels"
|
||||
service_selector_field="spec.selector"
|
||||
rc_replicas_field="spec.replicas"
|
||||
port_field="spec.port"
|
||||
fi
|
||||
|
||||
# Passing no arguments to create is an error
|
||||
@ -473,6 +475,26 @@ __EOF__
|
||||
# Post-condition: 3 replicas
|
||||
kube::test::get_object_assert 'rc frontend-controller' "{{.$rc_replicas_field}}" '3'
|
||||
|
||||
### Expose replication controller as service
|
||||
# Pre-condition: 3 replicas
|
||||
kube::test::get_object_assert 'rc frontend-controller' "{{.$rc_replicas_field}}" '3'
|
||||
# Command
|
||||
kubectl expose rc frontend-controller --port=80 "${kube_flags[@]}"
|
||||
# Post-condition: service exists
|
||||
kube::test::get_object_assert 'service frontend-controller' "{{.$port_field}}" '80'
|
||||
# Command
|
||||
kubectl expose service frontend-controller --port=443 --service-name=frontend-controller-2 "${kube_flags[@]}"
|
||||
# Post-condition: service exists
|
||||
kube::test::get_object_assert 'service frontend-controller-2' "{{.$port_field}}" '443'
|
||||
# Command
|
||||
kubectl create -f examples/limitrange/valid-pod.json "${kube_flags[@]}"
|
||||
kubectl expose pod valid-pod --port=444 --service-name=frontend-controller-3 "${kube_flags[@]}"
|
||||
# Post-condition: service exists
|
||||
kube::test::get_object_assert 'service frontend-controller-3' "{{.$port_field}}" '444'
|
||||
# Cleanup services
|
||||
kubectl delete pod valid-pod "${kube_flags[@]}"
|
||||
kubectl delete service frontend-controller{,-2,-3} "${kube_flags[@]}"
|
||||
|
||||
### Delete replication controller with id
|
||||
# Pre-condition: frontend replication controller is running
|
||||
kube::test::get_object_assert rc "{{range.items}}{{.$id_field}}:{{end}}" 'frontend-controller:'
|
||||
|
@ -45,6 +45,8 @@ const (
|
||||
// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
|
||||
// of resources and different API sets.
|
||||
// TODO: make the functions interfaces
|
||||
// TODO: pass the various interfaces on the factory directly into the command constructors (so the
|
||||
// commands are decoupled from the factory).
|
||||
type Factory struct {
|
||||
clients *clientCache
|
||||
flags *pflag.FlagSet
|
||||
@ -66,6 +68,9 @@ type Factory struct {
|
||||
Resizer func(mapping *meta.RESTMapping) (kubectl.Resizer, error)
|
||||
// Returns a Reaper for gracefully shutting down resources.
|
||||
Reaper func(mapping *meta.RESTMapping) (kubectl.Reaper, error)
|
||||
// PodSelectorForResource returns the pod selector associated with the provided resource name
|
||||
// or an error.
|
||||
PodSelectorForResource func(mapping *meta.RESTMapping, namespace, name string) (string, error)
|
||||
// Returns a schema that can validate objects stored on disk.
|
||||
Validator func() (validation.Schema, error)
|
||||
// Returns the default namespace to use in cases where no other namespace is specified
|
||||
@ -128,6 +133,41 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
||||
Printer: func(mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
|
||||
return kubectl.NewHumanReadablePrinter(noHeaders), nil
|
||||
},
|
||||
PodSelectorForResource: func(mapping *meta.RESTMapping, namespace, name string) (string, error) {
|
||||
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
|
||||
client, err := clients.ClientForVersion("")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
switch mapping.Kind {
|
||||
case "ReplicationController":
|
||||
rc, err := client.ReplicationControllers(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return kubectl.MakeLabels(rc.Spec.Selector), nil
|
||||
case "Pod":
|
||||
rc, err := client.Pods(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(rc.Labels) == 0 {
|
||||
return "", fmt.Errorf("the pod has no labels and cannot be exposed")
|
||||
}
|
||||
return kubectl.MakeLabels(rc.Labels), nil
|
||||
case "Service":
|
||||
rc, err := client.ReplicationControllers(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if rc.Spec.Selector == nil {
|
||||
return "", fmt.Errorf("the service has no pod selector set")
|
||||
}
|
||||
return kubectl.MakeLabels(rc.Spec.Selector), nil
|
||||
default:
|
||||
return "", fmt.Errorf("it is not possible to get a pod selector from %s", mapping.Kind)
|
||||
}
|
||||
},
|
||||
Resizer: func(mapping *meta.RESTMapping) (kubectl.Resizer, error) {
|
||||
client, err := clients.ClientForVersion(mapping.APIVersion)
|
||||
if err != nil {
|
||||
|
@ -29,19 +29,22 @@ import (
|
||||
const (
|
||||
expose_long = `Take a replicated application and expose it as Kubernetes Service.
|
||||
|
||||
Looks up a ReplicationController by name, and uses the selector for that replication controller
|
||||
as the selector for a new Service on the specified port.`
|
||||
Looks up a replication controller or service by name and uses the selector for that resource as the
|
||||
selector for a new Service on the specified port.`
|
||||
|
||||
expose_example = `// Creates a service for a replicated nginx, which serves on port 80 and connects to the containers on port 8000.
|
||||
$ kubectl expose nginx --port=80 --container-port=8000
|
||||
|
||||
// Creates a second service based on the above service, exposing the container port 8443 as port 443 with the name "nginx-https"
|
||||
$ kubectl expose service nginx --port=443 --container-port=8443 --service-name=nginx-https
|
||||
|
||||
// Create a service for a replicated streaming application on port 4100 balancing UDP traffic and named 'video-stream'.
|
||||
$ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream`
|
||||
)
|
||||
|
||||
func (f *Factory) NewCmdExposeService(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "expose NAME --port=port [--protocol=TCP|UDP] [--container-port=number-or-name] [--service-name=name] [--public-ip=ip] [--create-external-load-balancer=bool]",
|
||||
Use: "expose RESOURCE NAME --port=port [--protocol=TCP|UDP] [--container-port=number-or-name] [--service-name=name] [--public-ip=ip] [--create-external-load-balancer=bool]",
|
||||
Short: "Take a replicated application and expose it as Kubernetes Service",
|
||||
Long: expose_long,
|
||||
Example: expose_example,
|
||||
@ -66,8 +69,12 @@ func (f *Factory) NewCmdExposeService(out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
func RunExpose(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return util.UsageError(cmd, "<name> is required for expose")
|
||||
var name, resource string
|
||||
switch l := len(args); {
|
||||
case l == 2:
|
||||
resource, name = args[0], args[1]
|
||||
default:
|
||||
return util.UsageError(cmd, "the type and name of a resource to expose are required arguments")
|
||||
}
|
||||
|
||||
namespace, err := f.DefaultNamespace()
|
||||
@ -83,7 +90,7 @@ func RunExpose(f *Factory, out io.Writer, cmd *cobra.Command, args []string) err
|
||||
|
||||
generator, found := kubectl.Generators[generatorName]
|
||||
if !found {
|
||||
return util.UsageError(cmd, fmt.Sprintf("Generator: %s not found.", generator))
|
||||
return util.UsageError(cmd, fmt.Sprintf("generator %q not found.", generator))
|
||||
}
|
||||
if util.GetFlagInt(cmd, "port") < 1 {
|
||||
return util.UsageError(cmd, "--port is required and must be a positive integer.")
|
||||
@ -91,16 +98,25 @@ func RunExpose(f *Factory, out io.Writer, cmd *cobra.Command, args []string) err
|
||||
names := generator.ParamNames()
|
||||
params := kubectl.MakeParams(cmd, names)
|
||||
if len(util.GetFlagString(cmd, "service-name")) == 0 {
|
||||
params["name"] = args[0]
|
||||
params["name"] = name
|
||||
} else {
|
||||
params["name"] = util.GetFlagString(cmd, "service-name")
|
||||
}
|
||||
if _, found := params["selector"]; !found {
|
||||
rc, err := client.ReplicationControllers(namespace).Get(args[0])
|
||||
if s, found := params["selector"]; !found || len(s) == 0 {
|
||||
mapper, _ := f.Object()
|
||||
v, k, err := mapper.VersionAndKindForResource(resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params["selector"] = kubectl.MakeLabels(rc.Spec.Selector)
|
||||
mapping, err := mapper.RESTMapping(k, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s, err := f.PodSelectorForResource(mapping, namespace, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params["selector"] = s
|
||||
}
|
||||
if util.GetFlagBool(cmd, "create-external-load-balancer") {
|
||||
params["create-external-load-balancer"] = "true"
|
||||
|
Loading…
Reference in New Issue
Block a user