Enable v1beta3 API via --runtime_config=api/v1beta3 flag

This exposes the proper v1beta3 API endpoint when the user specifies
the --runtime_config=api/v1beta3 argument to the apiserver. v1beta3
is still considered experimental and subject to change.

--runtime_config is a map of string keys and values, that can be
specified by providing

    --runtime_config=a=b,b=c,d,e

Only the key must be specified, the value can be omitted.

Enables v1beta3 in hack/local-up-cluster.sh and hack/test-cmd.sh
This commit is contained in:
Clayton Coleman 2015-01-08 12:42:20 -05:00
parent 8262c30c97
commit 7fd887df61
7 changed files with 113 additions and 17 deletions

View File

@ -82,6 +82,7 @@ var (
allowPrivileged = flag.Bool("allow_privileged", false, "If true, allow privileged containers.") allowPrivileged = flag.Bool("allow_privileged", false, "If true, allow privileged containers.")
portalNet util.IPNet // TODO: make this a list portalNet util.IPNet // TODO: make this a list
enableLogsSupport = flag.Bool("enable_logs_support", true, "Enables server endpoint for log collection") enableLogsSupport = flag.Bool("enable_logs_support", true, "Enables server endpoint for log collection")
runtimeConfig util.ConfigurationMap
kubeletConfig = client.KubeletConfig{ kubeletConfig = client.KubeletConfig{
Port: 10250, Port: 10250,
EnableHttps: false, EnableHttps: false,
@ -89,10 +90,13 @@ var (
) )
func init() { func init() {
runtimeConfig = make(util.ConfigurationMap)
flag.Var(&address, "address", "The IP address on to serve on (set to 0.0.0.0 for all interfaces)") flag.Var(&address, "address", "The IP address on to serve on (set to 0.0.0.0 for all interfaces)")
flag.Var(&etcdServerList, "etcd_servers", "List of etcd servers to watch (http://ip:port), comma separated. Mutually exclusive with -etcd_config") flag.Var(&etcdServerList, "etcd_servers", "List of etcd servers to watch (http://ip:port), comma separated. Mutually exclusive with -etcd_config")
flag.Var(&corsAllowedOriginList, "cors_allowed_origins", "List of allowed origins for CORS, comma separated. An allowed origin can be a regular expression to support subdomain matching. If this list is empty CORS will not be enabled.") flag.Var(&corsAllowedOriginList, "cors_allowed_origins", "List of allowed origins for CORS, comma separated. An allowed origin can be a regular expression to support subdomain matching. If this list is empty CORS will not be enabled.")
flag.Var(&portalNet, "portal_net", "A CIDR notation IP range from which to assign portal IPs. This must not overlap with any IP ranges assigned to nodes for pods.") flag.Var(&portalNet, "portal_net", "A CIDR notation IP range from which to assign portal IPs. This must not overlap with any IP ranges assigned to nodes for pods.")
flag.Var(&runtimeConfig, "runtime_config", "A set of key=value pairs that describe runtime configuration that may be passed to the apiserver.")
client.BindKubeletClientConfigFlags(flag.CommandLine, &kubeletConfig) client.BindKubeletClientConfigFlags(flag.CommandLine, &kubeletConfig)
} }
@ -140,6 +144,8 @@ func main() {
glog.Fatalf("Failure to start kubelet client: %v", err) glog.Fatalf("Failure to start kubelet client: %v", err)
} }
_, v1beta3 := runtimeConfig["api/v1beta3"]
// TODO: expose same flags as client.BindClientConfigFlags but for a server // TODO: expose same flags as client.BindClientConfigFlags but for a server
clientConfig := &client.Config{ clientConfig := &client.Config{
Host: net.JoinHostPort(address.String(), strconv.Itoa(int(*port))), Host: net.JoinHostPort(address.String(), strconv.Itoa(int(*port))),
@ -189,6 +195,7 @@ func main() {
Authenticator: authenticator, Authenticator: authenticator,
Authorizer: authorizer, Authorizer: authorizer,
AdmissionControl: admissionController, AdmissionControl: admissionController,
EnableV1Beta3: v1beta3,
} }
m := master.New(config) m := master.New(config)

View File

@ -92,6 +92,7 @@ sudo "${GO_OUT}/kube-apiserver" \
-v=${LOG_LEVEL} \ -v=${LOG_LEVEL} \
--address="${API_HOST}" \ --address="${API_HOST}" \
--port="${API_PORT}" \ --port="${API_PORT}" \
--runtime_config=api/v1beta3 \
--etcd_servers="http://127.0.0.1:4001" \ --etcd_servers="http://127.0.0.1:4001" \
--portal_net="10.0.0.0/24" \ --portal_net="10.0.0.0/24" \
--cors_allowed_origins="${API_CORS_ALLOWED_ORIGINS}" >"${APISERVER_LOG}" 2>&1 & --cors_allowed_origins="${API_CORS_ALLOWED_ORIGINS}" >"${APISERVER_LOG}" 2>&1 &

View File

@ -74,6 +74,7 @@ kube::log::status "Starting kube-apiserver"
--etcd_servers="http://${ETCD_HOST}:${ETCD_PORT}" \ --etcd_servers="http://${ETCD_HOST}:${ETCD_PORT}" \
--public_address_override="127.0.0.1" \ --public_address_override="127.0.0.1" \
--kubelet_port=${KUBELET_PORT} \ --kubelet_port=${KUBELET_PORT} \
--runtime_config=api/v1beta3 \
--portal_net="10.0.0.0/24" 1>&2 & --portal_net="10.0.0.0/24" 1>&2 &
APISERVER_PID=$! APISERVER_PID=$!
@ -87,7 +88,7 @@ kube::log::status "Starting CONTROLLER-MANAGER"
CTLRMGR_PID=$! CTLRMGR_PID=$!
kube::util::wait_for_url "http://127.0.0.1:${CTLRMGR_PORT}/healthz" "controller-manager: " kube::util::wait_for_url "http://127.0.0.1:${CTLRMGR_PORT}/healthz" "controller-manager: "
kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/api/v1beta1/minions/127.0.0.1" "apiserver(minions): " kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/api/v1beta1/minions/127.0.0.1" "apiserver(minions): " 0.2 25
kube_cmd=( kube_cmd=(
"${KUBE_OUTPUT_HOSTBIN}/kubectl" "${KUBE_OUTPUT_HOSTBIN}/kubectl"
@ -95,6 +96,7 @@ kube_cmd=(
kube_api_versions=( kube_api_versions=(
v1beta1 v1beta1
v1beta2 v1beta2
v1beta3
) )
for version in "${kube_api_versions[@]}"; do for version in "${kube_api_versions[@]}"; do
kube_flags=( kube_flags=(
@ -103,7 +105,7 @@ for version in "${kube_api_versions[@]}"; do
--api-version="${version}" --api-version="${version}"
) )
kube::log::status "Testing kubectl(pods)" kube::log::status "Testing kubectl(${version}:pods)"
"${kube_cmd[@]}" get pods "${kube_flags[@]}" "${kube_cmd[@]}" get pods "${kube_flags[@]}"
"${kube_cmd[@]}" create -f examples/guestbook/redis-master.json "${kube_flags[@]}" "${kube_cmd[@]}" create -f examples/guestbook/redis-master.json "${kube_flags[@]}"
"${kube_cmd[@]}" get pods "${kube_flags[@]}" "${kube_cmd[@]}" get pods "${kube_flags[@]}"
@ -111,33 +113,36 @@ for version in "${kube_api_versions[@]}"; do
[[ "$("${kube_cmd[@]}" get pod redis-master -o template --output-version=v1beta1 -t '{{ .id }}' "${kube_flags[@]}")" == "redis-master" ]] [[ "$("${kube_cmd[@]}" get pod redis-master -o template --output-version=v1beta1 -t '{{ .id }}' "${kube_flags[@]}")" == "redis-master" ]]
output_pod=$("${kube_cmd[@]}" get pod redis-master -o json --output-version=v1beta1 "${kube_flags[@]}") output_pod=$("${kube_cmd[@]}" get pod redis-master -o json --output-version=v1beta1 "${kube_flags[@]}")
"${kube_cmd[@]}" delete pod redis-master "${kube_flags[@]}" "${kube_cmd[@]}" delete pod redis-master "${kube_flags[@]}"
before="$("${kube_cmd[@]}" get pods -o template -t '{{ len .items }}' "${kube_flags[@]}")" before="$("${kube_cmd[@]}" get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
echo $output_pod | "${kube_cmd[@]}" create -f - "${kube_flags[@]}" echo $output_pod | "${kube_cmd[@]}" create -f - "${kube_flags[@]}"
[[ "$("${kube_cmd[@]}" get pods -o template -t \"{{ len .items - ${before}}}\" "${kube_flags[@]}")" -eq 1 ]] after="$("${kube_cmd[@]}" get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
"${kube_cmd[@]}" get pods -o yaml "${kube_flags[@]}" | grep -q "id: redis-master" [[ "$((${after} - ${before}))" -eq 1 ]]
"${kube_cmd[@]}" get pods -o yaml --output-version=v1beta1 "${kube_flags[@]}" | grep -q "id: redis-master"
"${kube_cmd[@]}" describe pod redis-master "${kube_flags[@]}" | grep -q 'Name:.*redis-master' "${kube_cmd[@]}" describe pod redis-master "${kube_flags[@]}" | grep -q 'Name:.*redis-master'
"${kube_cmd[@]}" delete -f examples/guestbook/redis-master.json "${kube_flags[@]}" "${kube_cmd[@]}" delete -f examples/guestbook/redis-master.json "${kube_flags[@]}"
kube::log::status "Testing kubectl(services)" kube::log::status "Testing kubectl(${version}:services)"
"${kube_cmd[@]}" get services "${kube_flags[@]}" "${kube_cmd[@]}" get services "${kube_flags[@]}"
"${kube_cmd[@]}" create -f examples/guestbook/frontend-service.json "${kube_flags[@]}" "${kube_cmd[@]}" create -f examples/guestbook/frontend-service.json "${kube_flags[@]}"
"${kube_cmd[@]}" get services "${kube_flags[@]}" "${kube_cmd[@]}" get services "${kube_flags[@]}"
"${kube_cmd[@]}" delete service frontend "${kube_flags[@]}" "${kube_cmd[@]}" delete service frontend "${kube_flags[@]}"
kube::log::status "Testing kubectl(replicationcontrollers)" kube::log::status "Testing kubectl(${version}:replicationcontrollers)"
"${kube_cmd[@]}" get replicationcontrollers "${kube_flags[@]}" "${kube_cmd[@]}" get replicationcontrollers "${kube_flags[@]}"
"${kube_cmd[@]}" create -f examples/guestbook/frontend-controller.json "${kube_flags[@]}" "${kube_cmd[@]}" create -f examples/guestbook/frontend-controller.json "${kube_flags[@]}"
"${kube_cmd[@]}" get replicationcontrollers "${kube_flags[@]}" "${kube_cmd[@]}" get replicationcontrollers "${kube_flags[@]}"
"${kube_cmd[@]}" describe replicationcontroller frontendController "${kube_flags[@]}" | grep -q 'Replicas:.*3 desired' "${kube_cmd[@]}" describe replicationcontroller frontendController "${kube_flags[@]}" | grep -q 'Replicas:.*3 desired'
"${kube_cmd[@]}" delete rc frontendController "${kube_flags[@]}" "${kube_cmd[@]}" delete rc frontendController "${kube_flags[@]}"
kube::log::status "Testing kubectl(minions)" kube::log::status "Testing kubectl(${version}:nodes)"
"${kube_cmd[@]}" get minions "${kube_flags[@]}"
"${kube_cmd[@]}" get minions 127.0.0.1 "${kube_flags[@]}"
kube::log::status "Testing kubectl(nodes)"
"${kube_cmd[@]}" get nodes "${kube_flags[@]}" "${kube_cmd[@]}" get nodes "${kube_flags[@]}"
"${kube_cmd[@]}" describe nodes 127.0.0.1 "${kube_flags[@]}" "${kube_cmd[@]}" describe nodes 127.0.0.1 "${kube_flags[@]}"
if [[ "${version}" != "v1beta3" ]]; then
kube::log::status "Testing kubectl(${version}:minions)"
"${kube_cmd[@]}" get minions "${kube_flags[@]}"
"${kube_cmd[@]}" get minions 127.0.0.1 "${kube_flags[@]}"
fi
done done
kube::log::status "TEST PASSED" kube::log::status "TEST PASSED"

View File

@ -24,6 +24,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
@ -38,7 +39,7 @@ const OldestVersion = "v1beta1"
// may be assumed to be least feature rich to most feature rich, and clients may // may be assumed to be least feature rich to most feature rich, and clients may
// choose to prefer the latter items in the list over the former items when presented // choose to prefer the latter items in the list over the former items when presented
// with a set of versions to choose. // with a set of versions to choose.
var Versions = []string{"v1beta1", "v1beta2"} var Versions = []string{"v1beta1", "v1beta2", "v1beta3"}
// Codec is the default codec for serializing output that should use // Codec is the default codec for serializing output that should use
// the latest supported version. Use this Codec when writing to // the latest supported version. Use this Codec when writing to
@ -80,6 +81,12 @@ func InterfacesFor(version string) (*meta.VersionInterfaces, error) {
ObjectConvertor: api.Scheme, ObjectConvertor: api.Scheme,
MetadataAccessor: accessor, MetadataAccessor: accessor,
}, nil }, nil
case "v1beta3":
return &meta.VersionInterfaces{
Codec: v1beta3.Codec,
ObjectConvertor: api.Scheme,
MetadataAccessor: accessor,
}, nil
default: default:
return nil, fmt.Errorf("unsupported storage version: %s (valid: %s)", version, strings.Join(Versions, ", ")) return nil, fmt.Errorf("unsupported storage version: %s (valid: %s)", version, strings.Join(Versions, ", "))
} }
@ -96,7 +103,7 @@ func init() {
return interfaces, true return interfaces, true
}, },
) )
mapper.Add(api.Scheme, true, Versions...) mapper.Add(api.Scheme, true, "v1beta1", "v1beta2")
// TODO: when v1beta3 is added it will not use mixed case. mapper.Add(api.Scheme, false, "v1beta3")
RESTMapper = mapper RESTMapper = mapper
} }

View File

@ -27,6 +27,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
) )
@ -40,7 +41,8 @@ func TestSetsCodec(t *testing.T) {
"v1beta1": {false, "/api/v1beta1/", v1beta1.Codec}, "v1beta1": {false, "/api/v1beta1/", v1beta1.Codec},
"": {false, "/api/v1beta1/", v1beta1.Codec}, "": {false, "/api/v1beta1/", v1beta1.Codec},
"v1beta2": {false, "/api/v1beta2/", v1beta2.Codec}, "v1beta2": {false, "/api/v1beta2/", v1beta2.Codec},
"v1beta3": {true, "", nil}, "v1beta3": {false, "/api/v1beta3/", v1beta3.Codec},
"v1beta4": {true, "", nil},
} }
for version, expected := range testCases { for version, expected := range testCases {
client, err := New(&Config{Host: "127.0.0.1", Version: version}) client, err := New(&Config{Host: "127.0.0.1", Version: version})

View File

@ -33,6 +33,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator"
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer"
@ -72,6 +73,7 @@ type Config struct {
EnableLogsSupport bool EnableLogsSupport bool
EnableUISupport bool EnableUISupport bool
EnableSwaggerSupport bool EnableSwaggerSupport bool
EnableV1Beta3 bool
APIPrefix string APIPrefix string
CorsAllowedOriginList util.StringList CorsAllowedOriginList util.StringList
Authenticator authenticator.Request Authenticator authenticator.Request
@ -122,6 +124,7 @@ type Master struct {
authorizer authorizer.Authorizer authorizer authorizer.Authorizer
admissionControl admission.Interface admissionControl admission.Interface
masterCount int masterCount int
v1beta3 bool
readOnlyServer string readOnlyServer string
readWriteServer string readWriteServer string
@ -252,6 +255,7 @@ func New(c *Config) *Master {
authenticator: c.Authenticator, authenticator: c.Authenticator,
authorizer: c.Authorizer, authorizer: c.Authorizer,
admissionControl: c.AdmissionControl, admissionControl: c.AdmissionControl,
v1beta3: c.EnableV1Beta3,
masterCount: c.MasterCount, masterCount: c.MasterCount,
readOnlyServer: net.JoinHostPort(c.PublicAddress, strconv.Itoa(int(c.ReadOnlyPort))), readOnlyServer: net.JoinHostPort(c.PublicAddress, strconv.Itoa(int(c.ReadOnlyPort))),
@ -353,11 +357,16 @@ func (m *Master) init(c *Config) {
"bindings": binding.NewREST(m.bindingRegistry), "bindings": binding.NewREST(m.bindingRegistry),
} }
versionHandler := apiserver.APIVersionHandler("v1beta1", "v1beta2")
apiserver.NewAPIGroupVersion(m.API_v1beta1()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta1") apiserver.NewAPIGroupVersion(m.API_v1beta1()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta1")
apiserver.NewAPIGroupVersion(m.API_v1beta2()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta2") apiserver.NewAPIGroupVersion(m.API_v1beta2()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta2")
if c.EnableV1Beta3 {
apiserver.NewAPIGroupVersion(m.API_v1beta3()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta3")
versionHandler = apiserver.APIVersionHandler("v1beta1", "v1beta2", "v1beta3")
}
// TODO: InstallREST should register each version automatically // TODO: InstallREST should register each version automatically
versionHandler := apiserver.APIVersionHandler("v1beta1", "v1beta2")
m.rootWebService.Route(m.rootWebService.GET(c.APIPrefix).To(versionHandler)) m.rootWebService.Route(m.rootWebService.GET(c.APIPrefix).To(versionHandler))
apiserver.InstallSupport(m.handlerContainer, m.rootWebService) apiserver.InstallSupport(m.handlerContainer, m.rootWebService)
@ -482,3 +491,15 @@ func (m *Master) API_v1beta2() (map[string]apiserver.RESTStorage, runtime.Codec,
} }
return storage, v1beta2.Codec, "/api/v1beta2", latest.SelfLinker, m.admissionControl return storage, v1beta2.Codec, "/api/v1beta2", latest.SelfLinker, m.admissionControl
} }
// API_v1beta3 returns the resources and codec for API version v1beta3.
func (m *Master) API_v1beta3() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker, admission.Interface) {
storage := make(map[string]apiserver.RESTStorage)
for k, v := range m.storage {
if k == "minions" {
continue
}
storage[strings.ToLower(k)] = v
}
return storage, v1beta3.Codec, "/api/v1beta3", latest.SelfLinker, m.admissionControl
}

View File

@ -0,0 +1,53 @@
/*
Copyright 2014 Google Inc. All rights reserved.
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"
"sort"
"strings"
)
type ConfigurationMap map[string]string
func (m *ConfigurationMap) String() string {
pairs := []string{}
for k, v := range *m {
pairs = append(pairs, fmt.Sprintf("%s=%s", k, v))
}
sort.Strings(pairs)
return strings.Join(pairs, ",")
}
func (m *ConfigurationMap) Set(value string) error {
for _, s := range strings.Split(value, ",") {
if len(s) == 0 {
continue
}
arr := strings.SplitN(s, "=", 2)
if len(arr) == 2 {
(*m)[arr[0]] = arr[1]
} else {
(*m)[arr[0]] = ""
}
}
return nil
}
func (*ConfigurationMap) Type() string {
return "mapStringString"
}