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
pull/6/head
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.")
portalNet util.IPNet // TODO: make this a list
enableLogsSupport = flag.Bool("enable_logs_support", true, "Enables server endpoint for log collection")
runtimeConfig util.ConfigurationMap
kubeletConfig = client.KubeletConfig{
Port: 10250,
EnableHttps: false,
@ -89,10 +90,13 @@ var (
)
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(&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(&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)
}
@ -140,6 +144,8 @@ func main() {
glog.Fatalf("Failure to start kubelet client: %v", err)
}
_, v1beta3 := runtimeConfig["api/v1beta3"]
// TODO: expose same flags as client.BindClientConfigFlags but for a server
clientConfig := &client.Config{
Host: net.JoinHostPort(address.String(), strconv.Itoa(int(*port))),
@ -189,6 +195,7 @@ func main() {
Authenticator: authenticator,
Authorizer: authorizer,
AdmissionControl: admissionController,
EnableV1Beta3: v1beta3,
}
m := master.New(config)

View File

@ -92,6 +92,7 @@ sudo "${GO_OUT}/kube-apiserver" \
-v=${LOG_LEVEL} \
--address="${API_HOST}" \
--port="${API_PORT}" \
--runtime_config=api/v1beta3 \
--etcd_servers="http://127.0.0.1:4001" \
--portal_net="10.0.0.0/24" \
--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}" \
--public_address_override="127.0.0.1" \
--kubelet_port=${KUBELET_PORT} \
--runtime_config=api/v1beta3 \
--portal_net="10.0.0.0/24" 1>&2 &
APISERVER_PID=$!
@ -87,7 +88,7 @@ kube::log::status "Starting CONTROLLER-MANAGER"
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:${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_OUTPUT_HOSTBIN}/kubectl"
@ -95,6 +96,7 @@ kube_cmd=(
kube_api_versions=(
v1beta1
v1beta2
v1beta3
)
for version in "${kube_api_versions[@]}"; do
kube_flags=(
@ -103,7 +105,7 @@ for version in "${kube_api_versions[@]}"; do
--api-version="${version}"
)
kube::log::status "Testing kubectl(pods)"
kube::log::status "Testing kubectl(${version}:pods)"
"${kube_cmd[@]}" get pods "${kube_flags[@]}"
"${kube_cmd[@]}" create -f examples/guestbook/redis-master.json "${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" ]]
output_pod=$("${kube_cmd[@]}" get pod redis-master -o json --output-version=v1beta1 "${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[@]}"
[[ "$("${kube_cmd[@]}" get pods -o template -t \"{{ len .items - ${before}}}\" "${kube_flags[@]}")" -eq 1 ]]
"${kube_cmd[@]}" get pods -o yaml "${kube_flags[@]}" | grep -q "id: redis-master"
after="$("${kube_cmd[@]}" get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[[ "$((${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[@]}" 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[@]}" create -f examples/guestbook/frontend-service.json "${kube_flags[@]}"
"${kube_cmd[@]}" get services "${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[@]}" create -f examples/guestbook/frontend-controller.json "${kube_flags[@]}"
"${kube_cmd[@]}" get replicationcontrollers "${kube_flags[@]}"
"${kube_cmd[@]}" describe replicationcontroller frontendController "${kube_flags[@]}" | grep -q 'Replicas:.*3 desired'
"${kube_cmd[@]}" delete rc frontendController "${kube_flags[@]}"
kube::log::status "Testing kubectl(minions)"
"${kube_cmd[@]}" get minions "${kube_flags[@]}"
"${kube_cmd[@]}" get minions 127.0.0.1 "${kube_flags[@]}"
kube::log::status "Testing kubectl(nodes)"
kube::log::status "Testing kubectl(${version}:nodes)"
"${kube_cmd[@]}" get nodes "${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
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/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
"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
// choose to prefer the latter items in the list over the former items when presented
// 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
// the latest supported version. Use this Codec when writing to
@ -80,6 +81,12 @@ func InterfacesFor(version string) (*meta.VersionInterfaces, error) {
ObjectConvertor: api.Scheme,
MetadataAccessor: accessor,
}, nil
case "v1beta3":
return &meta.VersionInterfaces{
Codec: v1beta3.Codec,
ObjectConvertor: api.Scheme,
MetadataAccessor: accessor,
}, nil
default:
return nil, fmt.Errorf("unsupported storage version: %s (valid: %s)", version, strings.Join(Versions, ", "))
}
@ -96,7 +103,7 @@ func init() {
return interfaces, true
},
)
mapper.Add(api.Scheme, true, Versions...)
// TODO: when v1beta3 is added it will not use mixed case.
mapper.Add(api.Scheme, true, "v1beta1", "v1beta2")
mapper.Add(api.Scheme, false, "v1beta3")
RESTMapper = mapper
}

View File

@ -27,6 +27,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"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/util"
)
@ -40,7 +41,8 @@ func TestSetsCodec(t *testing.T) {
"v1beta1": {false, "/api/v1beta1/", v1beta1.Codec},
"": {false, "/api/v1beta1/", v1beta1.Codec},
"v1beta2": {false, "/api/v1beta2/", v1beta2.Codec},
"v1beta3": {true, "", nil},
"v1beta3": {false, "/api/v1beta3/", v1beta3.Codec},
"v1beta4": {true, "", nil},
}
for version, expected := range testCases {
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/v1beta1"
"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/auth/authenticator"
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer"
@ -72,6 +73,7 @@ type Config struct {
EnableLogsSupport bool
EnableUISupport bool
EnableSwaggerSupport bool
EnableV1Beta3 bool
APIPrefix string
CorsAllowedOriginList util.StringList
Authenticator authenticator.Request
@ -122,6 +124,7 @@ type Master struct {
authorizer authorizer.Authorizer
admissionControl admission.Interface
masterCount int
v1beta3 bool
readOnlyServer string
readWriteServer string
@ -252,6 +255,7 @@ func New(c *Config) *Master {
authenticator: c.Authenticator,
authorizer: c.Authorizer,
admissionControl: c.AdmissionControl,
v1beta3: c.EnableV1Beta3,
masterCount: c.MasterCount,
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),
}
versionHandler := apiserver.APIVersionHandler("v1beta1", "v1beta2")
apiserver.NewAPIGroupVersion(m.API_v1beta1()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta1")
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
versionHandler := apiserver.APIVersionHandler("v1beta1", "v1beta2")
m.rootWebService.Route(m.rootWebService.GET(c.APIPrefix).To(versionHandler))
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
}
// 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"
}