Implement basic admission control framework
This commit is contained in:
@@ -46,4 +46,9 @@
|
|||||||
{% endif -%}
|
{% endif -%}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
DAEMON_ARGS="{{daemon_args}} {{address}} {{etcd_servers}} {{ cloud_provider }} --allow_privileged={{pillar['allow_privileged']}} {{portal_net}} {{cert_file}} {{key_file}} {{secure_port}} {{token_auth_file}} {{publicAddressOverride}} {{pillar['log_level']}}"
|
{% set admission_control = "" -%}
|
||||||
|
{% if grains.admission_control is defined -%}
|
||||||
|
{% set admission_control = "-admission_control=" + grains.admission_control -%}
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
DAEMON_ARGS="{{daemon_args}} {{address}} {{etcd_servers}} {{ cloud_provider }} {{admission_control}} --allow_privileged={{pillar['allow_privileged']}} {{portal_net}} {{cert_file}} {{key_file}} {{secure_port}} {{token_auth_file}} {{publicAddressOverride}} {{pillar['log_level']}}"
|
||||||
|
@@ -75,6 +75,7 @@ grains:
|
|||||||
cloud_provider: vagrant
|
cloud_provider: vagrant
|
||||||
roles:
|
roles:
|
||||||
- kubernetes-master
|
- kubernetes-master
|
||||||
|
admission_control: AlwaysAdmit
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
mkdir -p /srv/salt-overlay/pillar
|
mkdir -p /srv/salt-overlay/pillar
|
||||||
|
@@ -27,6 +27,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
@@ -63,23 +64,25 @@ var (
|
|||||||
"File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). "+
|
"File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). "+
|
||||||
"If HTTPS serving is enabled, and --tls_cert_file and --tls_private_key_file are not provided, "+
|
"If HTTPS serving is enabled, and --tls_cert_file and --tls_private_key_file are not provided, "+
|
||||||
"a self-signed certificate and key are generated for the public address and saved to /var/run/kubernetes.")
|
"a self-signed certificate and key are generated for the public address and saved to /var/run/kubernetes.")
|
||||||
tlsPrivateKeyFile = flag.String("tls_private_key_file", "", "File containing x509 private key matching --tls_cert_file.")
|
tlsPrivateKeyFile = flag.String("tls_private_key_file", "", "File containing x509 private key matching --tls_cert_file.")
|
||||||
apiPrefix = flag.String("api_prefix", "/api", "The prefix for API requests on the server. Default '/api'.")
|
apiPrefix = flag.String("api_prefix", "/api", "The prefix for API requests on the server. Default '/api'.")
|
||||||
storageVersion = flag.String("storage_version", "", "The version to store resources with. Defaults to server preferred")
|
storageVersion = flag.String("storage_version", "", "The version to store resources with. Defaults to server preferred")
|
||||||
cloudProvider = flag.String("cloud_provider", "", "The provider for cloud services. Empty string for no provider.")
|
cloudProvider = flag.String("cloud_provider", "", "The provider for cloud services. Empty string for no provider.")
|
||||||
cloudConfigFile = flag.String("cloud_config", "", "The path to the cloud provider configuration file. Empty string for no configuration file.")
|
cloudConfigFile = flag.String("cloud_config", "", "The path to the cloud provider configuration file. Empty string for no configuration file.")
|
||||||
healthCheckMinions = flag.Bool("health_check_minions", true, "If true, health check minions and filter unhealthy ones. Default true.")
|
healthCheckMinions = flag.Bool("health_check_minions", true, "If true, health check minions and filter unhealthy ones. Default true.")
|
||||||
eventTTL = flag.Duration("event_ttl", 48*time.Hour, "Amount of time to retain events. Default 2 days.")
|
eventTTL = flag.Duration("event_ttl", 48*time.Hour, "Amount of time to retain events. Default 2 days.")
|
||||||
tokenAuthFile = flag.String("token_auth_file", "", "If set, the file that will be used to secure the secure port of the API server via token authentication.")
|
tokenAuthFile = flag.String("token_auth_file", "", "If set, the file that will be used to secure the secure port of the API server via token authentication.")
|
||||||
authorizationMode = flag.String("authorization_mode", "AlwaysAllow", "Selects how to do authorization on the secure port. One of: "+strings.Join(apiserver.AuthorizationModeChoices, ","))
|
authorizationMode = flag.String("authorization_mode", "AlwaysAllow", "Selects how to do authorization on the secure port. One of: "+strings.Join(apiserver.AuthorizationModeChoices, ","))
|
||||||
authorizationPolicyFile = flag.String("authorization_policy_file", "", "File with authorization policy in csv format, used with --authorization_mode=ABAC, on the secure port.")
|
authorizationPolicyFile = flag.String("authorization_policy_file", "", "File with authorization policy in csv format, used with --authorization_mode=ABAC, on the secure port.")
|
||||||
etcdServerList util.StringList
|
admissionControl = flag.String("admission_control", "AlwaysAdmit", "Ordered list of plug-ins to do admission control of resources into cluster. Comma-delimited list of: "+strings.Join(admission.GetPlugins(), ", "))
|
||||||
etcdConfigFile = flag.String("etcd_config", "", "The config file for the etcd client. Mutually exclusive with -etcd_servers.")
|
admissionControlConfigFile = flag.String("admission_control_config_file", "", "File with admission control configuration.")
|
||||||
corsAllowedOriginList util.StringList
|
etcdServerList util.StringList
|
||||||
allowPrivileged = flag.Bool("allow_privileged", false, "If true, allow privileged containers.")
|
etcdConfigFile = flag.String("etcd_config", "", "The config file for the etcd client. Mutually exclusive with -etcd_servers.")
|
||||||
portalNet util.IPNet // TODO: make this a list
|
corsAllowedOriginList util.StringList
|
||||||
enableLogsSupport = flag.Bool("enable_logs_support", true, "Enables server endpoint for log collection")
|
allowPrivileged = flag.Bool("allow_privileged", false, "If true, allow privileged containers.")
|
||||||
kubeletConfig = client.KubeletConfig{
|
portalNet util.IPNet // TODO: make this a list
|
||||||
|
enableLogsSupport = flag.Bool("enable_logs_support", true, "Enables server endpoint for log collection")
|
||||||
|
kubeletConfig = client.KubeletConfig{
|
||||||
Port: 10250,
|
Port: 10250,
|
||||||
EnableHttps: false,
|
EnableHttps: false,
|
||||||
}
|
}
|
||||||
@@ -164,6 +167,9 @@ func main() {
|
|||||||
glog.Fatalf("Invalid Authorization Config: %v", err)
|
glog.Fatalf("Invalid Authorization Config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
admissionControlPluginNames := strings.Split(*admissionControl, ",")
|
||||||
|
admissionController := admission.NewAdmissionControl(client, admissionControlPluginNames, *admissionControlConfigFile)
|
||||||
|
|
||||||
config := &master.Config{
|
config := &master.Config{
|
||||||
Client: client,
|
Client: client,
|
||||||
Cloud: cloud,
|
Cloud: cloud,
|
||||||
@@ -182,6 +188,7 @@ func main() {
|
|||||||
PublicAddress: *publicAddressOverride,
|
PublicAddress: *publicAddressOverride,
|
||||||
Authenticator: authenticator,
|
Authenticator: authenticator,
|
||||||
Authorizer: authorizer,
|
Authorizer: authorizer,
|
||||||
|
AdmissionControl: admissionController,
|
||||||
}
|
}
|
||||||
m := master.New(config)
|
m := master.New(config)
|
||||||
|
|
||||||
|
@@ -25,4 +25,7 @@ import (
|
|||||||
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/openstack"
|
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/openstack"
|
||||||
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/ovirt"
|
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/ovirt"
|
||||||
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/vagrant"
|
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/vagrant"
|
||||||
|
|
||||||
|
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/admit"
|
||||||
|
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/deny"
|
||||||
)
|
)
|
||||||
|
38
pkg/admission/admission_control.go
Normal file
38
pkg/admission/admission_control.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
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 admission
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type admissionController struct {
|
||||||
|
client client.Interface
|
||||||
|
admissionHandler Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdmissionControl(client client.Interface, pluginNames []string, configFilePath string) AdmissionControl {
|
||||||
|
return &admissionController{
|
||||||
|
client: client,
|
||||||
|
admissionHandler: newInterface(pluginNames, configFilePath),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *admissionController) AdmissionControl(operation, kind, namespace string, object runtime.Object) (err error) {
|
||||||
|
return ac.admissionHandler.Admit(NewAttributesRecord(ac.client, object, namespace, kind, operation))
|
||||||
|
}
|
60
pkg/admission/attributes.go
Normal file
60
pkg/admission/attributes.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
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 admission
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type attributesRecord struct {
|
||||||
|
client client.Interface
|
||||||
|
namespace string
|
||||||
|
kind string
|
||||||
|
operation string
|
||||||
|
object runtime.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAttributesRecord(client client.Interface, object runtime.Object, namespace, kind, operation string) Attributes {
|
||||||
|
return &attributesRecord{
|
||||||
|
client: client,
|
||||||
|
namespace: namespace,
|
||||||
|
kind: kind,
|
||||||
|
operation: operation,
|
||||||
|
object: object,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (record *attributesRecord) GetClient() client.Interface {
|
||||||
|
return record.client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (record *attributesRecord) GetNamespace() string {
|
||||||
|
return record.namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
func (record *attributesRecord) GetKind() string {
|
||||||
|
return record.kind
|
||||||
|
}
|
||||||
|
|
||||||
|
func (record *attributesRecord) GetOperation() string {
|
||||||
|
return record.operation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (record *attributesRecord) GetObject() runtime.Object {
|
||||||
|
return record.object
|
||||||
|
}
|
45
pkg/admission/chain.go
Normal file
45
pkg/admission/chain.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
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 admission
|
||||||
|
|
||||||
|
import ()
|
||||||
|
|
||||||
|
// chainAdmissionHandler is an instance of admission.Interface that performs admission control using a chain of admission handlers
|
||||||
|
type chainAdmissionHandler []Interface
|
||||||
|
|
||||||
|
// New returns an admission.Interface that will enforce admission control decisions
|
||||||
|
func newInterface(pluginNames []string, configFilePath string) Interface {
|
||||||
|
plugins := []Interface{}
|
||||||
|
for _, pluginName := range pluginNames {
|
||||||
|
plugin := InitPlugin(pluginName, configFilePath)
|
||||||
|
if plugin != nil {
|
||||||
|
plugins = append(plugins, plugin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chainAdmissionHandler(plugins)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admit performs an admission control check using a chain of handlers, and returns immediately on first error
|
||||||
|
func (admissionHandler chainAdmissionHandler) Admit(a Attributes) (err error) {
|
||||||
|
for _, handler := range admissionHandler {
|
||||||
|
err := handler.Admit(a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
43
pkg/admission/interfaces.go
Normal file
43
pkg/admission/interfaces.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
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 admission
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Attributes is an interface used by AdmissionController to get information about a request
|
||||||
|
// that is used to make an admission decision.
|
||||||
|
type Attributes interface {
|
||||||
|
GetClient() client.Interface
|
||||||
|
GetNamespace() string
|
||||||
|
GetKind() string
|
||||||
|
GetOperation() string
|
||||||
|
GetObject() runtime.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface is an abstract, pluggable interface for Admission Control decisions.
|
||||||
|
type Interface interface {
|
||||||
|
// Admit makes an admission decision based on the request attributes
|
||||||
|
Admit(a Attributes) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdmissionControl is responsible for performing Admission control decisions
|
||||||
|
type AdmissionControl interface {
|
||||||
|
AdmissionControl(operation, kind, namespace string, object runtime.Object) (err error)
|
||||||
|
}
|
106
pkg/admission/plugins.go
Normal file
106
pkg/admission/plugins.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
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 admission
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Factory is a function that returns a Interface for admission decisions.
|
||||||
|
// The config parameter provides an io.Reader handler to the factory in
|
||||||
|
// order to load specific configurations. If no configuration is provided
|
||||||
|
// the parameter is nil.
|
||||||
|
type Factory func(config io.Reader) (Interface, error)
|
||||||
|
|
||||||
|
// All registered admission options.
|
||||||
|
var pluginsMutex sync.Mutex
|
||||||
|
var plugins = make(map[string]Factory)
|
||||||
|
|
||||||
|
// GetPlugins enumerates the
|
||||||
|
func GetPlugins() []string {
|
||||||
|
pluginsMutex.Lock()
|
||||||
|
defer pluginsMutex.Unlock()
|
||||||
|
keys := []string{}
|
||||||
|
for k := range plugins {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterPlugin registers a plugin Factory by name. This
|
||||||
|
// is expected to happen during app startup.
|
||||||
|
func RegisterPlugin(name string, plugin Factory) {
|
||||||
|
pluginsMutex.Lock()
|
||||||
|
defer pluginsMutex.Unlock()
|
||||||
|
_, found := plugins[name]
|
||||||
|
if found {
|
||||||
|
glog.Fatalf("Admission plugin %q was registered twice", name)
|
||||||
|
}
|
||||||
|
glog.V(1).Infof("Registered admission plugin %q", name)
|
||||||
|
plugins[name] = plugin
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInterface creates an instance of the named plugin, or nil if
|
||||||
|
// the name is not known. The error return is only used if the named provider
|
||||||
|
// was known but failed to initialize. The config parameter specifies the
|
||||||
|
// io.Reader handler of the configuration file for the cloud provider, or nil
|
||||||
|
// for no configuation.
|
||||||
|
func GetPlugin(name string, config io.Reader) (Interface, error) {
|
||||||
|
pluginsMutex.Lock()
|
||||||
|
defer pluginsMutex.Unlock()
|
||||||
|
f, found := plugins[name]
|
||||||
|
if !found {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return f(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitPlugin creates an instance of the named interface
|
||||||
|
func InitPlugin(name string, configFilePath string) Interface {
|
||||||
|
var config *os.File
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
glog.Info("No admission plugin specified.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if configFilePath != "" {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
config, err = os.Open(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("Couldn't open admission plugin configuration %s: %#v",
|
||||||
|
configFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer config.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin, err := GetPlugin(name, config)
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("Couldn't init admission plugin %q: %v", name, err)
|
||||||
|
}
|
||||||
|
if plugin == nil {
|
||||||
|
glog.Fatalf("Unknown admission plugin: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin
|
||||||
|
}
|
@@ -27,6 +27,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
@@ -55,9 +56,9 @@ const (
|
|||||||
// Handle returns a Handler function that exposes the provided storage interfaces
|
// Handle returns a Handler function that exposes the provided storage interfaces
|
||||||
// as RESTful resources at prefix, serialized by codec, and also includes the support
|
// as RESTful resources at prefix, serialized by codec, and also includes the support
|
||||||
// http resources.
|
// http resources.
|
||||||
func Handle(storage map[string]RESTStorage, codec runtime.Codec, root string, version string, selfLinker runtime.SelfLinker) http.Handler {
|
func Handle(storage map[string]RESTStorage, codec runtime.Codec, root string, version string, selfLinker runtime.SelfLinker, admissionControl admission.AdmissionControl) http.Handler {
|
||||||
prefix := root + "/" + version
|
prefix := root + "/" + version
|
||||||
group := NewAPIGroupVersion(storage, codec, prefix, selfLinker)
|
group := NewAPIGroupVersion(storage, codec, prefix, selfLinker, admissionControl)
|
||||||
container := restful.NewContainer()
|
container := restful.NewContainer()
|
||||||
mux := container.ServeMux
|
mux := container.ServeMux
|
||||||
group.InstallREST(container, root, version)
|
group.InstallREST(container, root, version)
|
||||||
@@ -83,13 +84,14 @@ type APIGroupVersion struct {
|
|||||||
// This is a helper method for registering multiple sets of REST handlers under different
|
// This is a helper method for registering multiple sets of REST handlers under different
|
||||||
// prefixes onto a server.
|
// prefixes onto a server.
|
||||||
// TODO: add multitype codec serialization
|
// TODO: add multitype codec serialization
|
||||||
func NewAPIGroupVersion(storage map[string]RESTStorage, codec runtime.Codec, canonicalPrefix string, selfLinker runtime.SelfLinker) *APIGroupVersion {
|
func NewAPIGroupVersion(storage map[string]RESTStorage, codec runtime.Codec, canonicalPrefix string, selfLinker runtime.SelfLinker, admissionControl admission.AdmissionControl) *APIGroupVersion {
|
||||||
return &APIGroupVersion{RESTHandler{
|
return &APIGroupVersion{RESTHandler{
|
||||||
storage: storage,
|
storage: storage,
|
||||||
codec: codec,
|
codec: codec,
|
||||||
canonicalPrefix: canonicalPrefix,
|
canonicalPrefix: canonicalPrefix,
|
||||||
selfLinker: selfLinker,
|
selfLinker: selfLinker,
|
||||||
ops: NewOperations(),
|
ops: NewOperations(),
|
||||||
|
admissionControl: admissionControl,
|
||||||
// Delay just long enough to handle most simple write operations
|
// Delay just long enough to handle most simple write operations
|
||||||
asyncOpWait: time.Millisecond * 25,
|
asyncOpWait: time.Millisecond * 25,
|
||||||
}}
|
}}
|
||||||
|
@@ -30,6 +30,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
apierrs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
apierrs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||||
@@ -38,6 +39,7 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/admit"
|
||||||
)
|
)
|
||||||
|
|
||||||
func convert(obj runtime.Object) (runtime.Object, error) {
|
func convert(obj runtime.Object) (runtime.Object, error) {
|
||||||
@@ -53,6 +55,7 @@ var accessor = meta.NewAccessor()
|
|||||||
var versioner runtime.ResourceVersioner = accessor
|
var versioner runtime.ResourceVersioner = accessor
|
||||||
var selfLinker runtime.SelfLinker = accessor
|
var selfLinker runtime.SelfLinker = accessor
|
||||||
var mapper meta.RESTMapper
|
var mapper meta.RESTMapper
|
||||||
|
var admissionHandler admission.Interface
|
||||||
|
|
||||||
func interfacesFor(version string) (*meta.VersionInterfaces, error) {
|
func interfacesFor(version string) (*meta.VersionInterfaces, error) {
|
||||||
switch version {
|
switch version {
|
||||||
@@ -92,6 +95,7 @@ func init() {
|
|||||||
)
|
)
|
||||||
defMapper.Add(api.Scheme, true, versions...)
|
defMapper.Add(api.Scheme, true, versions...)
|
||||||
mapper = defMapper
|
mapper = defMapper
|
||||||
|
admissionHandler = admit.NewAlwaysAdmit()
|
||||||
}
|
}
|
||||||
|
|
||||||
type Simple struct {
|
type Simple struct {
|
||||||
@@ -262,7 +266,7 @@ func TestNotFound(t *testing.T) {
|
|||||||
}
|
}
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": &SimpleRESTStorage{},
|
"foo": &SimpleRESTStorage{},
|
||||||
}, codec, "/prefix", testVersion, selfLinker)
|
}, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
@@ -284,7 +288,7 @@ func TestNotFound(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestVersion(t *testing.T) {
|
func TestVersion(t *testing.T) {
|
||||||
handler := Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
@@ -319,7 +323,7 @@ func TestSimpleList(t *testing.T) {
|
|||||||
namespace: "other",
|
namespace: "other",
|
||||||
expectedSet: "/prefix/version/simple?namespace=other",
|
expectedSet: "/prefix/version/simple?namespace=other",
|
||||||
}
|
}
|
||||||
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -342,7 +346,7 @@ func TestErrorList(t *testing.T) {
|
|||||||
errors: map[string]error{"list": fmt.Errorf("test Error")},
|
errors: map[string]error{"list": fmt.Errorf("test Error")},
|
||||||
}
|
}
|
||||||
storage["simple"] = &simpleStorage
|
storage["simple"] = &simpleStorage
|
||||||
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -368,7 +372,7 @@ func TestNonEmptyList(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
storage["simple"] = &simpleStorage
|
storage["simple"] = &simpleStorage
|
||||||
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -414,7 +418,7 @@ func TestGet(t *testing.T) {
|
|||||||
expectedSet: "/prefix/version/simple/id",
|
expectedSet: "/prefix/version/simple/id",
|
||||||
}
|
}
|
||||||
storage["simple"] = &simpleStorage
|
storage["simple"] = &simpleStorage
|
||||||
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -439,7 +443,7 @@ func TestGetMissing(t *testing.T) {
|
|||||||
errors: map[string]error{"get": apierrs.NewNotFound("simple", "id")},
|
errors: map[string]error{"get": apierrs.NewNotFound("simple", "id")},
|
||||||
}
|
}
|
||||||
storage["simple"] = &simpleStorage
|
storage["simple"] = &simpleStorage
|
||||||
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -458,7 +462,7 @@ func TestDelete(t *testing.T) {
|
|||||||
simpleStorage := SimpleRESTStorage{}
|
simpleStorage := SimpleRESTStorage{}
|
||||||
ID := "id"
|
ID := "id"
|
||||||
storage["simple"] = &simpleStorage
|
storage["simple"] = &simpleStorage
|
||||||
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -481,7 +485,7 @@ func TestDeleteMissing(t *testing.T) {
|
|||||||
errors: map[string]error{"delete": apierrs.NewNotFound("simple", ID)},
|
errors: map[string]error{"delete": apierrs.NewNotFound("simple", ID)},
|
||||||
}
|
}
|
||||||
storage["simple"] = &simpleStorage
|
storage["simple"] = &simpleStorage
|
||||||
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -506,7 +510,7 @@ func TestUpdate(t *testing.T) {
|
|||||||
t: t,
|
t: t,
|
||||||
expectedSet: "/prefix/version/simple/" + ID,
|
expectedSet: "/prefix/version/simple/" + ID,
|
||||||
}
|
}
|
||||||
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -541,7 +545,7 @@ func TestUpdateMissing(t *testing.T) {
|
|||||||
errors: map[string]error{"update": apierrs.NewNotFound("simple", ID)},
|
errors: map[string]error{"update": apierrs.NewNotFound("simple", ID)},
|
||||||
}
|
}
|
||||||
storage["simple"] = &simpleStorage
|
storage["simple"] = &simpleStorage
|
||||||
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(storage, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -576,7 +580,7 @@ func TestCreate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": simpleStorage,
|
"foo": simpleStorage,
|
||||||
}, codec, "/prefix", testVersion, selfLinker)
|
}, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
handler.(*defaultAPIServer).group.handler.asyncOpWait = 0
|
handler.(*defaultAPIServer).group.handler.asyncOpWait = 0
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@@ -619,7 +623,7 @@ func TestCreateNotFound(t *testing.T) {
|
|||||||
// See https://github.com/GoogleCloudPlatform/kubernetes/pull/486#discussion_r15037092.
|
// See https://github.com/GoogleCloudPlatform/kubernetes/pull/486#discussion_r15037092.
|
||||||
errors: map[string]error{"create": apierrs.NewNotFound("simple", "id")},
|
errors: map[string]error{"create": apierrs.NewNotFound("simple", "id")},
|
||||||
},
|
},
|
||||||
}, codec, "/prefix", testVersion, selfLinker)
|
}, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
@@ -760,7 +764,7 @@ func TestAsyncDelayReturnsError(t *testing.T) {
|
|||||||
return nil, apierrs.NewAlreadyExists("foo", "bar")
|
return nil, apierrs.NewAlreadyExists("foo", "bar")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
handler := Handle(map[string]RESTStorage{"foo": &storage}, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(map[string]RESTStorage{"foo": &storage}, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
handler.(*defaultAPIServer).group.handler.asyncOpWait = time.Millisecond / 2
|
handler.(*defaultAPIServer).group.handler.asyncOpWait = time.Millisecond / 2
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@@ -784,7 +788,7 @@ func TestAsyncCreateError(t *testing.T) {
|
|||||||
name: "bar",
|
name: "bar",
|
||||||
expectedSet: "/prefix/version/foo/bar",
|
expectedSet: "/prefix/version/foo/bar",
|
||||||
}
|
}
|
||||||
handler := Handle(map[string]RESTStorage{"foo": &storage}, codec, "/prefix", testVersion, selfLinker)
|
handler := Handle(map[string]RESTStorage{"foo": &storage}, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
handler.(*defaultAPIServer).group.handler.asyncOpWait = 0
|
handler.(*defaultAPIServer).group.handler.asyncOpWait = 0
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@@ -884,7 +888,7 @@ func TestSyncCreateTimeout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": &storage,
|
"foo": &storage,
|
||||||
}, codec, "/prefix", testVersion, selfLinker)
|
}, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -916,7 +920,7 @@ func TestCORSAllowedOrigins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handler := CORS(
|
handler := CORS(
|
||||||
Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker),
|
Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker, admissionHandler)
|
||||||
allowedOriginRegexps, nil, nil, "true",
|
allowedOriginRegexps, nil, nil, "true",
|
||||||
)
|
)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
|
@@ -113,7 +113,7 @@ func TestOperationsList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": simpleStorage,
|
"foo": simpleStorage,
|
||||||
}, codec, "/prefix", "version", selfLinker)
|
}, codec, "/prefix", "version", selfLinker, admissionHandler)
|
||||||
handler.(*defaultAPIServer).group.handler.asyncOpWait = 0
|
handler.(*defaultAPIServer).group.handler.asyncOpWait = 0
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@@ -170,7 +170,7 @@ func TestOpGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": simpleStorage,
|
"foo": simpleStorage,
|
||||||
}, codec, "/prefix", "version", selfLinker)
|
}, codec, "/prefix", "version", selfLinker, admissionHandler)
|
||||||
handler.(*defaultAPIServer).group.handler.asyncOpWait = 0
|
handler.(*defaultAPIServer).group.handler.asyncOpWait = 0
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
@@ -182,7 +182,7 @@ func TestProxy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": simpleStorage,
|
"foo": simpleStorage,
|
||||||
}, codec, "/prefix", "version", selfLinker)
|
}, codec, "/prefix", "version", selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ func TestRedirect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": simpleStorage,
|
"foo": simpleStorage,
|
||||||
}, codec, "/prefix", "version", selfLinker)
|
}, codec, "/prefix", "version", selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
|
@@ -21,6 +21,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
@@ -31,12 +32,13 @@ import (
|
|||||||
|
|
||||||
// RESTHandler implements HTTP verbs on a set of RESTful resources identified by name.
|
// RESTHandler implements HTTP verbs on a set of RESTful resources identified by name.
|
||||||
type RESTHandler struct {
|
type RESTHandler struct {
|
||||||
storage map[string]RESTStorage
|
storage map[string]RESTStorage
|
||||||
codec runtime.Codec
|
codec runtime.Codec
|
||||||
canonicalPrefix string
|
canonicalPrefix string
|
||||||
selfLinker runtime.SelfLinker
|
selfLinker runtime.SelfLinker
|
||||||
ops *Operations
|
ops *Operations
|
||||||
asyncOpWait time.Duration
|
asyncOpWait time.Duration
|
||||||
|
admissionControl admission.AdmissionControl
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP handles requests to all RESTStorage objects.
|
// ServeHTTP handles requests to all RESTStorage objects.
|
||||||
@@ -205,6 +207,14 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
|||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// invoke admission control
|
||||||
|
err = h.admissionControl.AdmissionControl("CREATE", parts[0], namespace, obj)
|
||||||
|
if err != nil {
|
||||||
|
errorJSON(err, h.codec, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
out, err := storage.Create(ctx, obj)
|
out, err := storage.Create(ctx, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
@@ -218,6 +228,14 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
|||||||
notFound(w, req)
|
notFound(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// invoke admission control
|
||||||
|
err := h.admissionControl.AdmissionControl("DELETE", parts[0], namespace, nil)
|
||||||
|
if err != nil {
|
||||||
|
errorJSON(err, h.codec, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
out, err := storage.Delete(ctx, parts[1])
|
out, err := storage.Delete(ctx, parts[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
@@ -242,6 +260,14 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
|||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// invoke admission control
|
||||||
|
err = h.admissionControl.AdmissionControl("UPDATE", parts[0], namespace, obj)
|
||||||
|
if err != nil {
|
||||||
|
errorJSON(err, h.codec, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
out, err := storage.Update(ctx, obj)
|
out, err := storage.Update(ctx, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
|
@@ -50,7 +50,7 @@ func TestWatchWebsocket(t *testing.T) {
|
|||||||
_ = ResourceWatcher(simpleStorage) // Give compile error if this doesn't work.
|
_ = ResourceWatcher(simpleStorage) // Give compile error if this doesn't work.
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": simpleStorage,
|
"foo": simpleStorage,
|
||||||
}, codec, "/api", "version", selfLinker)
|
}, codec, "/api", "version", selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ func TestWatchHTTP(t *testing.T) {
|
|||||||
simpleStorage := &SimpleRESTStorage{}
|
simpleStorage := &SimpleRESTStorage{}
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": simpleStorage,
|
"foo": simpleStorage,
|
||||||
}, codec, "/api", "version", selfLinker)
|
}, codec, "/api", "version", selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
@@ -167,7 +167,7 @@ func TestWatchParamParsing(t *testing.T) {
|
|||||||
simpleStorage := &SimpleRESTStorage{}
|
simpleStorage := &SimpleRESTStorage{}
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": simpleStorage,
|
"foo": simpleStorage,
|
||||||
}, codec, "/api", "version", selfLinker)
|
}, codec, "/api", "version", selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ func TestWatchProtocolSelection(t *testing.T) {
|
|||||||
simpleStorage := &SimpleRESTStorage{}
|
simpleStorage := &SimpleRESTStorage{}
|
||||||
handler := Handle(map[string]RESTStorage{
|
handler := Handle(map[string]RESTStorage{
|
||||||
"foo": simpleStorage,
|
"foo": simpleStorage,
|
||||||
}, codec, "/api", "version", selfLinker)
|
}, codec, "/api", "version", selfLinker, admissionHandler)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
defer server.CloseClientConnections()
|
defer server.CloseClientConnections()
|
||||||
|
@@ -28,6 +28,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"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"
|
||||||
@@ -75,6 +76,7 @@ type Config struct {
|
|||||||
CorsAllowedOriginList util.StringList
|
CorsAllowedOriginList util.StringList
|
||||||
Authenticator authenticator.Request
|
Authenticator authenticator.Request
|
||||||
Authorizer authorizer.Authorizer
|
Authorizer authorizer.Authorizer
|
||||||
|
AdmissionControl admission.AdmissionControl
|
||||||
|
|
||||||
// If specified, all web services will be registered into this container
|
// If specified, all web services will be registered into this container
|
||||||
RestfulContainer *restful.Container
|
RestfulContainer *restful.Container
|
||||||
@@ -118,6 +120,7 @@ type Master struct {
|
|||||||
corsAllowedOriginList util.StringList
|
corsAllowedOriginList util.StringList
|
||||||
authenticator authenticator.Request
|
authenticator authenticator.Request
|
||||||
authorizer authorizer.Authorizer
|
authorizer authorizer.Authorizer
|
||||||
|
admissionControl admission.AdmissionControl
|
||||||
masterCount int
|
masterCount int
|
||||||
|
|
||||||
readOnlyServer string
|
readOnlyServer string
|
||||||
@@ -248,6 +251,7 @@ func New(c *Config) *Master {
|
|||||||
corsAllowedOriginList: c.CorsAllowedOriginList,
|
corsAllowedOriginList: c.CorsAllowedOriginList,
|
||||||
authenticator: c.Authenticator,
|
authenticator: c.Authenticator,
|
||||||
authorizer: c.Authorizer,
|
authorizer: c.Authorizer,
|
||||||
|
admissionControl: c.AdmissionControl,
|
||||||
|
|
||||||
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))),
|
||||||
@@ -462,19 +466,19 @@ func (m *Master) getServersToValidate(c *Config) map[string]apiserver.Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// API_v1beta1 returns the resources and codec for API version v1beta1.
|
// API_v1beta1 returns the resources and codec for API version v1beta1.
|
||||||
func (m *Master) API_v1beta1() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker) {
|
func (m *Master) API_v1beta1() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker, admission.AdmissionControl) {
|
||||||
storage := make(map[string]apiserver.RESTStorage)
|
storage := make(map[string]apiserver.RESTStorage)
|
||||||
for k, v := range m.storage {
|
for k, v := range m.storage {
|
||||||
storage[k] = v
|
storage[k] = v
|
||||||
}
|
}
|
||||||
return storage, v1beta1.Codec, "/api/v1beta1", latest.SelfLinker
|
return storage, v1beta1.Codec, "/api/v1beta1", latest.SelfLinker, m.admissionControl
|
||||||
}
|
}
|
||||||
|
|
||||||
// API_v1beta2 returns the resources and codec for API version v1beta2.
|
// API_v1beta2 returns the resources and codec for API version v1beta2.
|
||||||
func (m *Master) API_v1beta2() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker) {
|
func (m *Master) API_v1beta2() (map[string]apiserver.RESTStorage, runtime.Codec, string, runtime.SelfLinker, admission.AdmissionControl) {
|
||||||
storage := make(map[string]apiserver.RESTStorage)
|
storage := make(map[string]apiserver.RESTStorage)
|
||||||
for k, v := range m.storage {
|
for k, v := range m.storage {
|
||||||
storage[k] = v
|
storage[k] = v
|
||||||
}
|
}
|
||||||
return storage, v1beta2.Codec, "/api/v1beta2", latest.SelfLinker
|
return storage, v1beta2.Codec, "/api/v1beta2", latest.SelfLinker, m.admissionControl
|
||||||
}
|
}
|
||||||
|
38
plugin/pkg/admission/admit/admission.go
Normal file
38
plugin/pkg/admission/admit/admission.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
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 admit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
admission.RegisterPlugin("AlwaysAdmit", func(config io.Reader) (admission.Interface, error) { return NewAlwaysAdmit(), nil })
|
||||||
|
}
|
||||||
|
|
||||||
|
// alwaysAdmit is an implementation of admission.Interface which always says yes to an admit request.
|
||||||
|
// It is useful in tests and when using kubernetes in an open manner.
|
||||||
|
type alwaysAdmit struct{}
|
||||||
|
|
||||||
|
func (alwaysAdmit) Admit(a admission.Attributes) (err error) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAlwaysAdmit() admission.Interface {
|
||||||
|
return new(alwaysAdmit)
|
||||||
|
}
|
39
plugin/pkg/admission/deny/admission.go
Normal file
39
plugin/pkg/admission/deny/admission.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
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 deny
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
admission.RegisterPlugin("AlwaysDeny", func(config io.Reader) (admission.Interface, error) { return NewAlwaysDeny(), nil })
|
||||||
|
}
|
||||||
|
|
||||||
|
// alwaysDeny is an implementation of admission.Interface which always says no to an admission request.
|
||||||
|
// It is useful in unit tests to force an operation to be forbidden.
|
||||||
|
type alwaysDeny struct{}
|
||||||
|
|
||||||
|
func (alwaysDeny) Admit(a admission.Attributes) (err error) {
|
||||||
|
return errors.New("You shall not pass!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAlwaysDeny() admission.Interface {
|
||||||
|
return new(alwaysDeny)
|
||||||
|
}
|
Reference in New Issue
Block a user