Update dependencies
This commit is contained in:
191
vendor/tailscale.com/kube/kubeapi/api.go
generated
vendored
Normal file
191
vendor/tailscale.com/kube/kubeapi/api.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package kubeapi contains Kubernetes API types for internal consumption.
|
||||
// These types are split into a separate package for consumption of
|
||||
// non-Kubernetes shared libraries and binaries. Be mindful of not increasing
|
||||
// dependency size for those consumers when adding anything new here.
|
||||
package kubeapi
|
||||
|
||||
import "time"
|
||||
|
||||
// Note: The API types are copied from k8s.io/api{,machinery} to not introduce a
|
||||
// module dependency on the Kubernetes API as it pulls in many more dependencies.
|
||||
|
||||
// TypeMeta describes an individual object in an API response or request with
|
||||
// strings representing the type of the object and its API schema version.
|
||||
// Structures that are versioned or persisted should inline TypeMeta.
|
||||
type TypeMeta struct {
|
||||
// Kind is a string value representing the REST resource this object represents.
|
||||
// Servers may infer this from the endpoint the client submits requests to.
|
||||
// Cannot be updated.
|
||||
// In CamelCase.
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
// +optional
|
||||
Kind string `json:"kind,omitempty"`
|
||||
|
||||
// APIVersion defines the versioned schema of this representation of an object.
|
||||
// Servers should convert recognized schemas to the latest internal value, and
|
||||
// may reject unrecognized values.
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
// +optional
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
}
|
||||
|
||||
// ObjectMeta is metadata that all persisted resources must have, which
|
||||
// includes all objects users must create.
|
||||
type ObjectMeta struct {
|
||||
// Name must be unique within a namespace. Is required when creating resources, although
|
||||
// some resources may allow a client to request the generation of an appropriate name
|
||||
// automatically. Name is primarily intended for creation idempotence and configuration
|
||||
// definition.
|
||||
// Cannot be updated.
|
||||
// More info: http://kubernetes.io/docs/user-guide/identifiers#names
|
||||
// +optional
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace defines the space within which each name must be unique. An empty namespace is
|
||||
// equivalent to the "default" namespace, but "default" is the canonical representation.
|
||||
// Not all objects are required to be scoped to a namespace - the value of this field for
|
||||
// those objects will be empty.
|
||||
//
|
||||
// Must be a DNS_LABEL.
|
||||
// Cannot be updated.
|
||||
// More info: http://kubernetes.io/docs/user-guide/namespaces
|
||||
// +optional
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// UID is the unique in time and space value for this object. It is typically generated by
|
||||
// the server on successful creation of a resource and is not allowed to change on PUT
|
||||
// operations.
|
||||
//
|
||||
// Populated by the system.
|
||||
// Read-only.
|
||||
// More info: http://kubernetes.io/docs/user-guide/identifiers#uids
|
||||
// +optional
|
||||
UID string `json:"uid,omitempty"`
|
||||
|
||||
// An opaque value that represents the internal version of this object that can
|
||||
// be used by clients to determine when objects have changed. May be used for optimistic
|
||||
// concurrency, change detection, and the watch operation on a resource or set of resources.
|
||||
// Clients must treat these values as opaque and passed unmodified back to the server.
|
||||
// They may only be valid for a particular resource or set of resources.
|
||||
//
|
||||
// Populated by the system.
|
||||
// Read-only.
|
||||
// Value must be treated as opaque by clients and .
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||
// +optional
|
||||
ResourceVersion string `json:"resourceVersion,omitempty"`
|
||||
|
||||
// A sequence number representing a specific generation of the desired state.
|
||||
// Populated by the system. Read-only.
|
||||
// +optional
|
||||
Generation int64 `json:"generation,omitempty"`
|
||||
|
||||
// CreationTimestamp is a timestamp representing the server time when this object was
|
||||
// created. It is not guaranteed to be set in happens-before order across separate operations.
|
||||
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
|
||||
//
|
||||
// Populated by the system.
|
||||
// Read-only.
|
||||
// Null for lists.
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
// +optional
|
||||
CreationTimestamp time.Time `json:"creationTimestamp,omitempty"`
|
||||
|
||||
// DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This
|
||||
// field is set by the server when a graceful deletion is requested by the user, and is not
|
||||
// directly settable by a client. The resource is expected to be deleted (no longer visible
|
||||
// from resource lists, and not reachable by name) after the time in this field, once the
|
||||
// finalizers list is empty. As long as the finalizers list contains items, deletion is blocked.
|
||||
// Once the deletionTimestamp is set, this value may not be unset or be set further into the
|
||||
// future, although it may be shortened or the resource may be deleted prior to this time.
|
||||
// For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react
|
||||
// by sending a graceful termination signal to the containers in the pod. After that 30 seconds,
|
||||
// the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup,
|
||||
// remove the pod from the API. In the presence of network partitions, this object may still
|
||||
// exist after this timestamp, until an administrator or automated process can determine the
|
||||
// resource is fully terminated.
|
||||
// If not set, graceful deletion of the object has not been requested.
|
||||
//
|
||||
// Populated by the system when a graceful deletion is requested.
|
||||
// Read-only.
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
// +optional
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
|
||||
// Number of seconds allowed for this object to gracefully terminate before
|
||||
// it will be removed from the system. Only set when deletionTimestamp is also set.
|
||||
// May only be shortened.
|
||||
// Read-only.
|
||||
// +optional
|
||||
DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty"`
|
||||
|
||||
// Map of string keys and values that can be used to organize and categorize
|
||||
// (scope and select) objects. May match selectors of replication controllers
|
||||
// and services.
|
||||
// More info: http://kubernetes.io/docs/user-guide/labels
|
||||
// +optional
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// Annotations is an unstructured key value map stored with a resource that may be
|
||||
// set by external tools to store and retrieve arbitrary metadata. They are not
|
||||
// queryable and should be preserved when modifying objects.
|
||||
// More info: http://kubernetes.io/docs/user-guide/annotations
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// Secret holds secret data of a certain type. The total bytes of the values
|
||||
// in the Data field must be less than MaxSecretSize bytes.
|
||||
type Secret struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ObjectMeta `json:"metadata"`
|
||||
|
||||
// Data contains the secret data. Each key must consist of alphanumeric
|
||||
// characters, '-', '_' or '.'. The serialized form of the secret data is a
|
||||
// base64 encoded string, representing the arbitrary (possibly non-string)
|
||||
// data value here. Described in https://tools.ietf.org/html/rfc4648#section-4
|
||||
// +optional
|
||||
Data map[string][]byte `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// Status is a return value for calls that don't return other objects.
|
||||
type Status struct {
|
||||
TypeMeta `json:",inline"`
|
||||
// Status of the operation.
|
||||
// One of: "Success" or "Failure".
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
// +optional
|
||||
Status string `json:"status,omitempty"`
|
||||
|
||||
// A human-readable description of the status of this operation.
|
||||
// +optional
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
// A machine-readable description of why this operation is in the
|
||||
// "Failure" status. If this value is empty there
|
||||
// is no information available. A Reason clarifies an HTTP status
|
||||
// code but does not override it.
|
||||
// +optional
|
||||
Reason string `json:"reason,omitempty"`
|
||||
|
||||
// Extended data associated with the reason. Each reason may define its
|
||||
// own extended details. This field is optional and the data returned
|
||||
// is not guaranteed to conform to any schema except that defined by
|
||||
// the reason type.
|
||||
// +optional
|
||||
Details *struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
} `json:"details,omitempty"`
|
||||
|
||||
// Suggested HTTP return code for this status, 0 if not set.
|
||||
// +optional
|
||||
Code int `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
func (s *Status) Error() string {
|
||||
return s.Message
|
||||
}
|
||||
351
vendor/tailscale.com/kube/kubeclient/client.go
generated
vendored
Normal file
351
vendor/tailscale.com/kube/kubeclient/client.go
generated
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package kubeclient provides a client to interact with Kubernetes.
|
||||
// This package is Tailscale-internal and not meant for external consumption.
|
||||
// Further, the API should not be considered stable.
|
||||
// Client is split into a separate package for consumption of
|
||||
// non-Kubernetes shared libraries and binaries. Be mindful of not increasing
|
||||
// dependency size for those consumers when adding anything new here.
|
||||
package kubeclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"tailscale.com/kube/kubeapi"
|
||||
"tailscale.com/util/multierr"
|
||||
)
|
||||
|
||||
const (
|
||||
saPath = "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||
defaultURL = "https://kubernetes.default.svc"
|
||||
)
|
||||
|
||||
// rootPathForTests is set by tests to override the root path to the
|
||||
// service account directory.
|
||||
var rootPathForTests string
|
||||
|
||||
// SetRootPathForTesting sets the path to the service account directory.
|
||||
func SetRootPathForTesting(p string) {
|
||||
rootPathForTests = p
|
||||
}
|
||||
|
||||
func readFile(n string) ([]byte, error) {
|
||||
if rootPathForTests != "" {
|
||||
return os.ReadFile(filepath.Join(rootPathForTests, saPath, n))
|
||||
}
|
||||
return os.ReadFile(filepath.Join(saPath, n))
|
||||
}
|
||||
|
||||
// Client handles connections to Kubernetes.
|
||||
// It expects to be run inside a cluster.
|
||||
type Client interface {
|
||||
GetSecret(context.Context, string) (*kubeapi.Secret, error)
|
||||
UpdateSecret(context.Context, *kubeapi.Secret) error
|
||||
CreateSecret(context.Context, *kubeapi.Secret) error
|
||||
StrategicMergePatchSecret(context.Context, string, *kubeapi.Secret, string) error
|
||||
JSONPatchSecret(context.Context, string, []JSONPatch) error
|
||||
CheckSecretPermissions(context.Context, string) (bool, bool, error)
|
||||
SetDialer(dialer func(context.Context, string, string) (net.Conn, error))
|
||||
SetURL(string)
|
||||
}
|
||||
|
||||
type client struct {
|
||||
mu sync.Mutex
|
||||
url string
|
||||
ns string
|
||||
client *http.Client
|
||||
token string
|
||||
tokenExpiry time.Time
|
||||
}
|
||||
|
||||
// New returns a new client
|
||||
func New() (Client, error) {
|
||||
ns, err := readFile("namespace")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
caCert, err := readFile("ca.crt")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cp := x509.NewCertPool()
|
||||
if ok := cp.AppendCertsFromPEM(caCert); !ok {
|
||||
return nil, fmt.Errorf("kube: error in creating root cert pool")
|
||||
}
|
||||
return &client{
|
||||
url: defaultURL,
|
||||
ns: string(ns),
|
||||
client: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: cp,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetURL sets the URL to use for the Kubernetes API.
|
||||
// This is used only for testing.
|
||||
func (c *client) SetURL(url string) {
|
||||
c.url = url
|
||||
}
|
||||
|
||||
// SetDialer sets the dialer to use when establishing a connection
|
||||
// to the Kubernetes API server.
|
||||
func (c *client) SetDialer(dialer func(ctx context.Context, network, addr string) (net.Conn, error)) {
|
||||
c.client.Transport.(*http.Transport).DialContext = dialer
|
||||
}
|
||||
|
||||
func (c *client) expireToken() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.tokenExpiry = time.Now()
|
||||
}
|
||||
|
||||
func (c *client) getOrRenewToken() (string, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
tk, te := c.token, c.tokenExpiry
|
||||
if time.Now().Before(te) {
|
||||
return tk, nil
|
||||
}
|
||||
|
||||
tkb, err := readFile("token")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
c.token = string(tkb)
|
||||
c.tokenExpiry = time.Now().Add(30 * time.Minute)
|
||||
return c.token, nil
|
||||
}
|
||||
|
||||
func (c *client) secretURL(name string) string {
|
||||
if name == "" {
|
||||
return fmt.Sprintf("%s/api/v1/namespaces/%s/secrets", c.url, c.ns)
|
||||
}
|
||||
return fmt.Sprintf("%s/api/v1/namespaces/%s/secrets/%s", c.url, c.ns, name)
|
||||
}
|
||||
|
||||
func getError(resp *http.Response) error {
|
||||
if resp.StatusCode == 200 || resp.StatusCode == 201 {
|
||||
// These are the only success codes returned by the Kubernetes API.
|
||||
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#http-status-codes
|
||||
return nil
|
||||
}
|
||||
st := &kubeapi.Status{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(st); err != nil {
|
||||
return err
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
func setHeader(key, value string) func(*http.Request) {
|
||||
return func(req *http.Request) {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// doRequest performs an HTTP request to the Kubernetes API.
|
||||
// If in is not nil, it is expected to be a JSON-encodable object and will be
|
||||
// sent as the request body.
|
||||
// If out is not nil, it is expected to be a pointer to an object that can be
|
||||
// decoded from JSON.
|
||||
// If the request fails with a 401, the token is expired and a new one is
|
||||
// requested.
|
||||
func (c *client) doRequest(ctx context.Context, method, url string, in, out any, opts ...func(*http.Request)) error {
|
||||
req, err := c.newRequest(ctx, method, url, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(req)
|
||||
}
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if err := getError(resp); err != nil {
|
||||
if st, ok := err.(*kubeapi.Status); ok && st.Code == 401 {
|
||||
c.expireToken()
|
||||
}
|
||||
return err
|
||||
}
|
||||
if out != nil {
|
||||
return json.NewDecoder(resp.Body).Decode(out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) newRequest(ctx context.Context, method, url string, in any) (*http.Request, error) {
|
||||
tk, err := c.getOrRenewToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var body io.Reader
|
||||
if in != nil {
|
||||
switch in := in.(type) {
|
||||
case []byte:
|
||||
body = bytes.NewReader(in)
|
||||
default:
|
||||
var b bytes.Buffer
|
||||
if err := json.NewEncoder(&b).Encode(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = &b
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, method, url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if body != nil {
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
}
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("Authorization", "Bearer "+tk)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// GetSecret fetches the secret from the Kubernetes API.
|
||||
func (c *client) GetSecret(ctx context.Context, name string) (*kubeapi.Secret, error) {
|
||||
s := &kubeapi.Secret{Data: make(map[string][]byte)}
|
||||
if err := c.doRequest(ctx, "GET", c.secretURL(name), nil, s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// CreateSecret creates a secret in the Kubernetes API.
|
||||
func (c *client) CreateSecret(ctx context.Context, s *kubeapi.Secret) error {
|
||||
s.Namespace = c.ns
|
||||
return c.doRequest(ctx, "POST", c.secretURL(""), s, nil)
|
||||
}
|
||||
|
||||
// UpdateSecret updates a secret in the Kubernetes API.
|
||||
func (c *client) UpdateSecret(ctx context.Context, s *kubeapi.Secret) error {
|
||||
return c.doRequest(ctx, "PUT", c.secretURL(s.Name), s, nil)
|
||||
}
|
||||
|
||||
// JSONPatch is a JSON patch operation.
|
||||
// It currently (2023-03-02) only supports "add" and "remove" operations.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc6902
|
||||
type JSONPatch struct {
|
||||
Op string `json:"op"`
|
||||
Path string `json:"path"`
|
||||
Value any `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// JSONPatchSecret updates a secret in the Kubernetes API using a JSON patch.
|
||||
// It currently (2023-03-02) only supports "add" and "remove" operations.
|
||||
func (c *client) JSONPatchSecret(ctx context.Context, name string, patch []JSONPatch) error {
|
||||
for _, p := range patch {
|
||||
if p.Op != "remove" && p.Op != "add" && p.Op != "replace" {
|
||||
return fmt.Errorf("unsupported JSON patch operation: %q", p.Op)
|
||||
}
|
||||
}
|
||||
return c.doRequest(ctx, "PATCH", c.secretURL(name), patch, nil, setHeader("Content-Type", "application/json-patch+json"))
|
||||
}
|
||||
|
||||
// StrategicMergePatchSecret updates a secret in the Kubernetes API using a
|
||||
// strategic merge patch.
|
||||
// If a fieldManager is provided, it will be used to track the patch.
|
||||
func (c *client) StrategicMergePatchSecret(ctx context.Context, name string, s *kubeapi.Secret, fieldManager string) error {
|
||||
surl := c.secretURL(name)
|
||||
if fieldManager != "" {
|
||||
uv := url.Values{
|
||||
"fieldManager": {fieldManager},
|
||||
}
|
||||
surl += "?" + uv.Encode()
|
||||
}
|
||||
s.Namespace = c.ns
|
||||
s.Name = name
|
||||
return c.doRequest(ctx, "PATCH", surl, s, nil, setHeader("Content-Type", "application/strategic-merge-patch+json"))
|
||||
}
|
||||
|
||||
// CheckSecretPermissions checks the secret access permissions of the current
|
||||
// pod. It returns an error if the basic permissions tailscale needs are
|
||||
// missing, and reports whether the patch and create permissions are additionally present.
|
||||
//
|
||||
// Errors encountered during the access checking process are logged, but ignored
|
||||
// so that the pod tries to fail alive if the permissions exist and there's just
|
||||
// something wrong with SelfSubjectAccessReviews. There shouldn't be, pods
|
||||
// should always be able to use SSARs to assess their own permissions, but since
|
||||
// we didn't use to check permissions this way we'll be cautious in case some
|
||||
// old version of k8s deviates from the current behavior.
|
||||
func (c *client) CheckSecretPermissions(ctx context.Context, secretName string) (canPatch, canCreate bool, err error) {
|
||||
var errs []error
|
||||
for _, verb := range []string{"get", "update"} {
|
||||
ok, err := c.checkPermission(ctx, verb, secretName)
|
||||
if err != nil {
|
||||
log.Printf("error checking %s permission on secret %s: %v", verb, secretName, err)
|
||||
} else if !ok {
|
||||
errs = append(errs, fmt.Errorf("missing %s permission on secret %q", verb, secretName))
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return false, false, multierr.New(errs...)
|
||||
}
|
||||
canPatch, err = c.checkPermission(ctx, "patch", secretName)
|
||||
if err != nil {
|
||||
log.Printf("error checking patch permission on secret %s: %v", secretName, err)
|
||||
return false, false, nil
|
||||
}
|
||||
canCreate, err = c.checkPermission(ctx, "create", secretName)
|
||||
if err != nil {
|
||||
log.Printf("error checking create permission on secret %s: %v", secretName, err)
|
||||
return false, false, nil
|
||||
}
|
||||
return canPatch, canCreate, nil
|
||||
}
|
||||
|
||||
// checkPermission reports whether the current pod has permission to use the
|
||||
// given verb (e.g. get, update, patch, create) on secretName.
|
||||
func (c *client) checkPermission(ctx context.Context, verb, secretName string) (bool, error) {
|
||||
sar := map[string]any{
|
||||
"apiVersion": "authorization.k8s.io/v1",
|
||||
"kind": "SelfSubjectAccessReview",
|
||||
"spec": map[string]any{
|
||||
"resourceAttributes": map[string]any{
|
||||
"namespace": c.ns,
|
||||
"verb": verb,
|
||||
"resource": "secrets",
|
||||
"name": secretName,
|
||||
},
|
||||
},
|
||||
}
|
||||
var res struct {
|
||||
Status struct {
|
||||
Allowed bool `json:"allowed"`
|
||||
} `json:"status"`
|
||||
}
|
||||
url := c.url + "/apis/authorization.k8s.io/v1/selfsubjectaccessreviews"
|
||||
if err := c.doRequest(ctx, "POST", url, sar, &res); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return res.Status.Allowed, nil
|
||||
}
|
||||
|
||||
func IsNotFoundErr(err error) bool {
|
||||
if st, ok := err.(*kubeapi.Status); ok && st.Code == 404 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
36
vendor/tailscale.com/kube/kubeclient/fake_client.go
generated
vendored
Normal file
36
vendor/tailscale.com/kube/kubeclient/fake_client.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package kubeclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"tailscale.com/kube/kubeapi"
|
||||
)
|
||||
|
||||
var _ Client = &FakeClient{}
|
||||
|
||||
type FakeClient struct {
|
||||
GetSecretImpl func(context.Context, string) (*kubeapi.Secret, error)
|
||||
CheckSecretPermissionsImpl func(ctx context.Context, name string) (bool, bool, error)
|
||||
}
|
||||
|
||||
func (fc *FakeClient) CheckSecretPermissions(ctx context.Context, name string) (bool, bool, error) {
|
||||
return fc.CheckSecretPermissionsImpl(ctx, name)
|
||||
}
|
||||
func (fc *FakeClient) GetSecret(ctx context.Context, name string) (*kubeapi.Secret, error) {
|
||||
return fc.GetSecretImpl(ctx, name)
|
||||
}
|
||||
func (fc *FakeClient) SetURL(_ string) {}
|
||||
func (fc *FakeClient) SetDialer(dialer func(ctx context.Context, network, addr string) (net.Conn, error)) {
|
||||
}
|
||||
func (fc *FakeClient) StrategicMergePatchSecret(context.Context, string, *kubeapi.Secret, string) error {
|
||||
return nil
|
||||
}
|
||||
func (fc *FakeClient) JSONPatchSecret(context.Context, string, []JSONPatch) error {
|
||||
return nil
|
||||
}
|
||||
func (fc *FakeClient) UpdateSecret(context.Context, *kubeapi.Secret) error { return nil }
|
||||
func (fc *FakeClient) CreateSecret(context.Context, *kubeapi.Secret) error { return nil }
|
||||
55
vendor/tailscale.com/kube/kubetypes/grants.go
generated
vendored
Normal file
55
vendor/tailscale.com/kube/kubetypes/grants.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package kubetypes contains types and constants related to the Tailscale
|
||||
// Kubernetes Operator.
|
||||
// These are split into a separate package for consumption of
|
||||
// non-Kubernetes shared libraries and binaries. Be mindful of not increasing
|
||||
// dependency size for those consumers when adding anything new here.
|
||||
package kubetypes
|
||||
|
||||
import "net/netip"
|
||||
|
||||
// KubernetesCapRule is a rule provided via PeerCapabilityKubernetes capability.
|
||||
type KubernetesCapRule struct {
|
||||
// Impersonate is a list of rules that specify how to impersonate the caller
|
||||
// when proxying to the Kubernetes API.
|
||||
Impersonate *ImpersonateRule `json:"impersonate,omitempty"`
|
||||
// Recorders defines a tag of a tsrecorder instance(s) that a recording
|
||||
// of a 'kubectl exec' session, matching `src` of this grant, to an API
|
||||
// server proxy, matching `dst` of this grant, should be sent to.
|
||||
// This list must not contain more than one tag. The field
|
||||
// name matches the `Recorder` field with equal semantics for Tailscale
|
||||
// SSH session recorder. This field is set by users in ACL grants and is
|
||||
// then parsed by control, which resolves the tags and populates `RecorderAddrs``.
|
||||
// https://tailscale.com/kb/1246/tailscale-ssh-session-recording#turn-on-session-recording-in-acls
|
||||
Recorders []string `json:"recorder,omitempty"`
|
||||
// RecorderAddrs is a list of addresses that should be addresses of one
|
||||
// or more tsrecorder instance(s). If set, any `kubectl exec` session
|
||||
// from a client matching `src` of this grant to an API server proxy
|
||||
// matching `dst` of this grant will be recorded and the recording will
|
||||
// be sent to the tsrecorder. This field does not exist in the user
|
||||
// provided ACL grants - it is populated by control, which obtains the
|
||||
// addresses by resolving the tags provided via `Recorders` field.
|
||||
RecorderAddrs []netip.AddrPort `json:"recorderAddrs,omitempty"`
|
||||
// EnforceRecorder defines whether a kubectl exec session from a client
|
||||
// matching `src` to an API server proxy matching `dst` should fail
|
||||
// closed if it cannot be recorded (i.e if no recorder can be reached).
|
||||
// Default is to fail open.
|
||||
// The field name matches `EnforceRecorder` field with equal semantics for Tailscale SSH
|
||||
// session recorder.
|
||||
// https://tailscale.com/kb/1246/tailscale-ssh-session-recording#turn-on-session-recording-in-acls
|
||||
EnforceRecorder bool `json:"enforceRecorder,omitempty"`
|
||||
}
|
||||
|
||||
// ImpersonateRule defines how a request from the tailnet identity matching
|
||||
// 'src' of this grant should be impersonated.
|
||||
type ImpersonateRule struct {
|
||||
// Groups can be used to set a list of groups that a request to
|
||||
// Kubernetes API server should be impersonated as from. Groups in
|
||||
// Kubernetes only exist as subjects that RBAC rules refer to. Caller
|
||||
// can choose to use an existing group, such as system:masters, or
|
||||
// create RBAC for a new group.
|
||||
// https://kubernetes.io/docs/reference/access-authn-authz/rbac/#referring-to-subjects
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
}
|
||||
26
vendor/tailscale.com/kube/kubetypes/metrics.go
generated
vendored
Normal file
26
vendor/tailscale.com/kube/kubetypes/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package kubetypes
|
||||
|
||||
const (
|
||||
// Hostinfo App values for the Tailscale Kubernetes Operator components.
|
||||
AppOperator = "k8s-operator"
|
||||
AppAPIServerProxy = "k8s-operator-proxy"
|
||||
AppIngressProxy = "k8s-operator-ingress-proxy"
|
||||
AppIngressResource = "k8s-operator-ingress-resource"
|
||||
AppEgressProxy = "k8s-operator-egress-proxy"
|
||||
AppConnector = "k8s-operator-connector-resource"
|
||||
|
||||
// Clientmetrics for Tailscale Kubernetes Operator components
|
||||
MetricIngressProxyCount = "k8s_ingress_proxies" // L3
|
||||
MetricIngressResourceCount = "k8s_ingress_resources" // L7
|
||||
MetricEgressProxyCount = "k8s_egress_proxies"
|
||||
MetricConnectorResourceCount = "k8s_connector_resources"
|
||||
MetricConnectorWithSubnetRouterCount = "k8s_connector_subnetrouter_resources"
|
||||
MetricConnectorWithExitNodeCount = "k8s_connector_exitnode_resources"
|
||||
MetricNameserverCount = "k8s_nameserver_resources"
|
||||
MetricRecorderCount = "k8s_recorder_resources"
|
||||
MetricEgressServiceCount = "k8s_egress_service_resources"
|
||||
MetricProxyGroupCount = "k8s_proxygroup_resources"
|
||||
)
|
||||
Reference in New Issue
Block a user