248
vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/plane.go
generated
vendored
Normal file
248
vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/plane.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
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 controlplane
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
kerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/testing/certs"
|
||||
)
|
||||
|
||||
// NewTinyCA creates a new a tiny CA utility for provisioning serving certs and client certs FOR TESTING ONLY.
|
||||
// Don't use this for anything else!
|
||||
var NewTinyCA = certs.NewTinyCA
|
||||
|
||||
// ControlPlane is a struct that knows how to start your test control plane.
|
||||
//
|
||||
// Right now, that means Etcd and your APIServer. This is likely to increase in
|
||||
// future.
|
||||
type ControlPlane struct {
|
||||
APIServer *APIServer
|
||||
Etcd *Etcd
|
||||
|
||||
// Kubectl will override the default asset search path for kubectl
|
||||
KubectlPath string
|
||||
|
||||
// for the deprecated methods (Kubectl, etc)
|
||||
defaultUserCfg *rest.Config
|
||||
defaultUserKubectl *KubeCtl
|
||||
}
|
||||
|
||||
// Start will start your control plane processes. To stop them, call Stop().
|
||||
func (f *ControlPlane) Start() error {
|
||||
if f.Etcd == nil {
|
||||
f.Etcd = &Etcd{}
|
||||
}
|
||||
if err := f.Etcd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.APIServer == nil {
|
||||
f.APIServer = &APIServer{}
|
||||
}
|
||||
f.APIServer.EtcdURL = f.Etcd.URL
|
||||
if err := f.APIServer.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// provision the default user -- can be removed when the related
|
||||
// methods are removed. The default user has admin permissions to
|
||||
// mimic legacy no-authz setups.
|
||||
user, err := f.AddUser(User{Name: "default", Groups: []string{"system:masters"}}, &rest.Config{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to provision the default (legacy) user: %w", err)
|
||||
}
|
||||
kubectl, err := user.Kubectl()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to provision the default (legacy) kubeconfig: %w", err)
|
||||
}
|
||||
f.defaultUserCfg = user.Config()
|
||||
f.defaultUserKubectl = kubectl
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop will stop your control plane processes, and clean up their data.
|
||||
func (f *ControlPlane) Stop() error {
|
||||
var errList []error
|
||||
|
||||
if f.APIServer != nil {
|
||||
if err := f.APIServer.Stop(); err != nil {
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
if f.Etcd != nil {
|
||||
if err := f.Etcd.Stop(); err != nil {
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
|
||||
return kerrors.NewAggregate(errList)
|
||||
}
|
||||
|
||||
// APIURL returns the URL you should connect to to talk to your API server.
|
||||
//
|
||||
// If insecure serving is configured, this will contain the insecure port.
|
||||
// Otherwise, it will contain the secure port.
|
||||
//
|
||||
// Deprecated: use AddUser instead, or APIServer.{Ins|S}ecureServing.URL if
|
||||
// you really want just the URL.
|
||||
func (f *ControlPlane) APIURL() *url.URL {
|
||||
return f.APIServer.URL
|
||||
}
|
||||
|
||||
// KubeCtl returns a pre-configured KubeCtl, ready to connect to this
|
||||
// ControlPlane.
|
||||
//
|
||||
// Deprecated: use AddUser & AuthenticatedUser.Kubectl instead.
|
||||
func (f *ControlPlane) KubeCtl() *KubeCtl {
|
||||
return f.defaultUserKubectl
|
||||
}
|
||||
|
||||
// RESTClientConfig returns a pre-configured restconfig, ready to connect to
|
||||
// this ControlPlane.
|
||||
//
|
||||
// Deprecated: use AddUser & AuthenticatedUser.Config instead.
|
||||
func (f *ControlPlane) RESTClientConfig() (*rest.Config, error) {
|
||||
return f.defaultUserCfg, nil
|
||||
}
|
||||
|
||||
// AuthenticatedUser contains access information for an provisioned user,
|
||||
// including REST config, kubeconfig contents, and access to a KubeCtl instance.
|
||||
//
|
||||
// It's not "safe" to use the methods on this till after the API server has been
|
||||
// started (due to certificate initialization and such). The various methods will
|
||||
// panic if this is done.
|
||||
type AuthenticatedUser struct {
|
||||
// cfg is the rest.Config for connecting to the API server. It's lazily initialized.
|
||||
cfg *rest.Config
|
||||
// cfgIsComplete indicates the cfg has had late-initialized fields (e.g.
|
||||
// API server CA data) initialized.
|
||||
cfgIsComplete bool
|
||||
|
||||
// apiServer is a handle to the APIServer that's used when finalizing cfg
|
||||
// and producing the kubectl instance.
|
||||
plane *ControlPlane
|
||||
|
||||
// kubectl is our existing, provisioned kubectl. We don't provision one
|
||||
// till someone actually asks for it.
|
||||
kubectl *KubeCtl
|
||||
}
|
||||
|
||||
// Config returns the REST config that can be used to connect to the API server
|
||||
// as this user.
|
||||
//
|
||||
// Will panic if used before the API server is started.
|
||||
func (u *AuthenticatedUser) Config() *rest.Config {
|
||||
// NB(directxman12): we choose to panic here for ergonomics sake, and because there's
|
||||
// not really much you can do to "handle" this error. This machinery is intended to be
|
||||
// used in tests anyway, so panicing is not a particularly big deal.
|
||||
if u.cfgIsComplete {
|
||||
return u.cfg
|
||||
}
|
||||
if len(u.plane.APIServer.SecureServing.CA) == 0 {
|
||||
panic("the API server has not yet been started, please do that before accessing connection details")
|
||||
}
|
||||
|
||||
u.cfg.CAData = u.plane.APIServer.SecureServing.CA
|
||||
u.cfg.Host = u.plane.APIServer.SecureServing.URL("https", "/").String()
|
||||
u.cfgIsComplete = true
|
||||
return u.cfg
|
||||
}
|
||||
|
||||
// KubeConfig returns a KubeConfig that's roughly equivalent to this user's REST config.
|
||||
//
|
||||
// Will panic if used before the API server is started.
|
||||
func (u AuthenticatedUser) KubeConfig() ([]byte, error) {
|
||||
// NB(directxman12): we don't return the actual API object to avoid yet another
|
||||
// piece of kubernetes API in our public API, and also because generally the thing
|
||||
// you want to do with this is just write it out to a file for external debugging
|
||||
// purposes, etc.
|
||||
return KubeConfigFromREST(u.Config())
|
||||
}
|
||||
|
||||
// Kubectl returns a KubeCtl instance for talking to the API server as this user. It uses
|
||||
// a kubeconfig equivalent to that returned by .KubeConfig.
|
||||
//
|
||||
// Will panic if used before the API server is started.
|
||||
func (u *AuthenticatedUser) Kubectl() (*KubeCtl, error) {
|
||||
if u.kubectl != nil {
|
||||
return u.kubectl, nil
|
||||
}
|
||||
if len(u.plane.APIServer.CertDir) == 0 {
|
||||
panic("the API server has not yet been started, please do that before accessing connection details")
|
||||
}
|
||||
|
||||
// cleaning this up is handled when our tmpDir is deleted
|
||||
out, err := os.CreateTemp(u.plane.APIServer.CertDir, "*.kubecfg")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create file for kubeconfig: %w", err)
|
||||
}
|
||||
defer out.Close()
|
||||
contents, err := KubeConfigFromREST(u.Config())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := out.Write(contents); err != nil {
|
||||
return nil, fmt.Errorf("unable to write kubeconfig to disk at %s: %w", out.Name(), err)
|
||||
}
|
||||
k := &KubeCtl{
|
||||
Path: u.plane.KubectlPath,
|
||||
}
|
||||
k.Opts = append(k.Opts, fmt.Sprintf("--kubeconfig=%s", out.Name()))
|
||||
u.kubectl = k
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// AddUser provisions a new user in the cluster. It uses the APIServer's authentication
|
||||
// strategy -- see APIServer.SecureServing.Authn.
|
||||
//
|
||||
// Unlike AddUser, it's safe to pass a nil rest.Config here if you have no
|
||||
// particular opinions about the config.
|
||||
//
|
||||
// The default authentication strategy is not guaranteed to any specific strategy, but it is
|
||||
// guaranteed to be callable both before and after Start has been called (but, as noted in the
|
||||
// AuthenticatedUser docs, the given user objects are only valid after Start has been called).
|
||||
func (f *ControlPlane) AddUser(user User, baseConfig *rest.Config) (*AuthenticatedUser, error) {
|
||||
if f.GetAPIServer().SecureServing.Authn == nil {
|
||||
return nil, fmt.Errorf("no API server authentication is configured yet. The API server defaults one when Start is called, did you mean to use that?")
|
||||
}
|
||||
|
||||
if baseConfig == nil {
|
||||
baseConfig = &rest.Config{}
|
||||
}
|
||||
cfg, err := f.GetAPIServer().SecureServing.AddUser(user, baseConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AuthenticatedUser{
|
||||
cfg: cfg,
|
||||
plane: f,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAPIServer returns this ControlPlane's APIServer, initializing it if necessary.
|
||||
func (f *ControlPlane) GetAPIServer() *APIServer {
|
||||
if f.APIServer == nil {
|
||||
f.APIServer = &APIServer{}
|
||||
}
|
||||
return f.APIServer
|
||||
}
|
||||
Reference in New Issue
Block a user