mirror of https://github.com/kubernetes/kops.git
256 lines
7.2 KiB
Go
256 lines
7.2 KiB
Go
/*
|
|
Copyright 2019 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 vfsclientset
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"k8s.io/klog/v2"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/kops/pkg/apis/kops"
|
|
"k8s.io/kops/pkg/apis/kops/registry"
|
|
kopsinternalversion "k8s.io/kops/pkg/client/clientset_generated/clientset/typed/kops/internalversion"
|
|
"k8s.io/kops/pkg/client/simple"
|
|
"k8s.io/kops/upup/pkg/fi"
|
|
"k8s.io/kops/upup/pkg/fi/secrets"
|
|
"k8s.io/kops/util/pkg/vfs"
|
|
)
|
|
|
|
type VFSClientset struct {
|
|
vfsContext *vfs.VFSContext
|
|
basePath vfs.Path
|
|
}
|
|
|
|
var _ simple.Clientset = &VFSClientset{}
|
|
|
|
func (c *VFSClientset) VFSContext() *vfs.VFSContext {
|
|
return c.vfsContext
|
|
}
|
|
|
|
func (c *VFSClientset) clusters() *ClusterVFS {
|
|
return newClusterVFS(c.VFSContext(), c.basePath)
|
|
}
|
|
|
|
// GetCluster implements the GetCluster method of simple.Clientset for a VFS-backed state store
|
|
func (c *VFSClientset) GetCluster(ctx context.Context, name string) (*kops.Cluster, error) {
|
|
ctx, span := tracer.Start(ctx, "VFSClientset::GetCluster")
|
|
defer span.End()
|
|
|
|
return c.clusters().Get(ctx, name, metav1.GetOptions{})
|
|
}
|
|
|
|
// UpdateCluster implements the UpdateCluster method of simple.Clientset for a VFS-backed state store
|
|
func (c *VFSClientset) UpdateCluster(ctx context.Context, cluster *kops.Cluster, status *kops.ClusterStatus) (*kops.Cluster, error) {
|
|
return c.clusters().Update(cluster, status)
|
|
}
|
|
|
|
// CreateCluster implements the CreateCluster method of simple.Clientset for a VFS-backed state store
|
|
func (c *VFSClientset) CreateCluster(ctx context.Context, cluster *kops.Cluster) (*kops.Cluster, error) {
|
|
return c.clusters().Create(cluster)
|
|
}
|
|
|
|
// ListClusters implements the ListClusters method of simple.Clientset for a VFS-backed state store
|
|
func (c *VFSClientset) ListClusters(ctx context.Context, options metav1.ListOptions) (*kops.ClusterList, error) {
|
|
ctx, span := tracer.Start(ctx, "VFSClientset::ListClusters")
|
|
defer span.End()
|
|
|
|
return c.clusters().List(ctx, options)
|
|
}
|
|
|
|
// ConfigBaseFor implements the ConfigBaseFor method of simple.Clientset for a VFS-backed state store
|
|
func (c *VFSClientset) ConfigBaseFor(cluster *kops.Cluster) (vfs.Path, error) {
|
|
if cluster.Spec.ConfigStore.Base != "" {
|
|
return c.VFSContext().BuildVfsPath(cluster.Spec.ConfigStore.Base)
|
|
}
|
|
return c.clusters().configBase(cluster.Name)
|
|
}
|
|
|
|
// InstanceGroupsFor implements the InstanceGroupsFor method of simple.Clientset for a VFS-backed state store
|
|
func (c *VFSClientset) InstanceGroupsFor(cluster *kops.Cluster) kopsinternalversion.InstanceGroupInterface {
|
|
return newInstanceGroupVFS(c, cluster)
|
|
}
|
|
|
|
func (c *VFSClientset) AddonsFor(cluster *kops.Cluster) simple.AddonsClient {
|
|
return newAddonsVFS(c, cluster)
|
|
}
|
|
|
|
func (c *VFSClientset) SecretStore(cluster *kops.Cluster) (fi.SecretStore, error) {
|
|
if cluster.Spec.ConfigStore.Secrets == "" {
|
|
configBase, err := registry.ConfigBase(c.VFSContext(), cluster)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
basedir := configBase.Join("secrets")
|
|
return secrets.NewVFSSecretStore(cluster, basedir), nil
|
|
} else {
|
|
storePath, err := c.VFSContext().BuildVfsPath(cluster.Spec.ConfigStore.Secrets)
|
|
return secrets.NewVFSSecretStore(cluster, storePath), err
|
|
}
|
|
}
|
|
|
|
func (c *VFSClientset) KeyStore(cluster *kops.Cluster) (fi.CAStore, error) {
|
|
basedir, err := c.pkiPath(cluster)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
klog.V(8).Infof("Using keystore path: %q", basedir)
|
|
|
|
return fi.NewVFSCAStore(cluster, basedir), err
|
|
}
|
|
|
|
func (c *VFSClientset) SSHCredentialStore(cluster *kops.Cluster) (fi.SSHCredentialStore, error) {
|
|
basedir, err := c.pkiPath(cluster)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return fi.NewVFSSSHCredentialStore(cluster, basedir), nil
|
|
}
|
|
|
|
func (c *VFSClientset) pkiPath(cluster *kops.Cluster) (vfs.Path, error) {
|
|
if cluster.Spec.ConfigStore.Keypairs == "" {
|
|
configBase, err := registry.ConfigBase(c.VFSContext(), cluster)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return configBase.Join("pki"), nil
|
|
} else {
|
|
storePath, err := c.VFSContext().BuildVfsPath(cluster.Spec.ConfigStore.Keypairs)
|
|
return storePath, err
|
|
}
|
|
}
|
|
|
|
func DeleteAllClusterState(ctx context.Context, basePath vfs.Path) error {
|
|
paths, err := basePath.ReadTree(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("error listing files in state store: %v", err)
|
|
}
|
|
|
|
for _, path := range paths {
|
|
relativePath, err := vfs.RelativePath(basePath, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if relativePath == "" {
|
|
continue
|
|
}
|
|
|
|
// "cluster.spec" was written by kOps 1.21 and earlier.
|
|
if relativePath == "config" || relativePath == "cluster.spec" || relativePath == "cluster-completed.spec" || relativePath == registry.PathKopsVersionUpdated {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(relativePath, "addons/") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(relativePath, "clusteraddons/") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(relativePath, "pki/") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(relativePath, "secrets/") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(relativePath, "instancegroup/") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(relativePath, "igconfig/") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(relativePath, "manifests/") {
|
|
continue
|
|
}
|
|
// TODO: offer an option _not_ to delete backups?
|
|
if strings.HasPrefix(relativePath, "backups/") {
|
|
continue
|
|
}
|
|
|
|
return fmt.Errorf("refusing to delete: unknown file found: %s", path)
|
|
}
|
|
|
|
err = basePath.RemoveAll(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("error deleting cluster files in %s: %w", basePath, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *VFSClientset) DeleteCluster(ctx context.Context, cluster *kops.Cluster) error {
|
|
if cluster.Spec.ServiceAccountIssuerDiscovery != nil {
|
|
discoveryStore := cluster.Spec.ServiceAccountIssuerDiscovery.DiscoveryStore
|
|
if discoveryStore != "" {
|
|
path, err := c.VFSContext().BuildVfsPath(discoveryStore)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = path.Join("openid/v1/jwks").Remove(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = path.Join(".well-known/openid-configuration").Remove(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
secretStore := cluster.Spec.ConfigStore.Secrets
|
|
if secretStore != "" {
|
|
path, err := c.VFSContext().BuildVfsPath(secretStore)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = path.RemoveAll(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
keyStore := cluster.Spec.ConfigStore.Keypairs
|
|
if keyStore != "" && keyStore != secretStore {
|
|
path, err := c.VFSContext().BuildVfsPath(keyStore)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = path.RemoveAll(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
configBase, err := registry.ConfigBase(c.VFSContext(), cluster)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return DeleteAllClusterState(ctx, configBase)
|
|
}
|
|
|
|
func NewVFSClientset(vfsContext *vfs.VFSContext, basePath vfs.Path) simple.Clientset {
|
|
vfsClientset := &VFSClientset{
|
|
vfsContext: vfsContext,
|
|
basePath: basePath,
|
|
}
|
|
return vfsClientset
|
|
}
|