mirror of https://github.com/kubernetes/kops.git
metal: more functions to enable `kops update cluster`
We still have to add VMs, but this is another step closer.
This commit is contained in:
parent
d5c56dcf98
commit
08481f512c
|
@ -20,7 +20,12 @@ set -o pipefail
|
|||
set -o xtrace
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
cd ${REPO_ROOT}/tests/e2e/scenarios/bare-metal
|
||||
cd ${REPO_ROOT}
|
||||
|
||||
BINDIR=${REPO_ROOT}/.build/bin
|
||||
go build -o ${BINDIR}/kops ./cmd/kops
|
||||
|
||||
KOPS=${BINDIR}/kops
|
||||
|
||||
function cleanup() {
|
||||
echo "running dump-artifacts"
|
||||
|
@ -32,7 +37,7 @@ function cleanup() {
|
|||
|
||||
trap cleanup EXIT
|
||||
|
||||
./start-vms
|
||||
${REPO_ROOT}/tests/e2e/scenarios/bare-metal/start-vms
|
||||
|
||||
echo "Waiting 30 seconds for VMs to start"
|
||||
sleep 30
|
||||
|
@ -64,14 +69,25 @@ aws --version
|
|||
aws --endpoint-url=${S3_ENDPOINT} s3 mb s3://kops-state-store
|
||||
|
||||
# List clusters (there should not be any yet)
|
||||
go run ./cmd/kops get cluster || true
|
||||
${KOPS} get cluster || true
|
||||
|
||||
# Create a cluster
|
||||
go run ./cmd/kops create cluster --cloud=metal metal.k8s.local --zones main
|
||||
${KOPS} create cluster --cloud=metal metal.k8s.local --zones main
|
||||
|
||||
# Set the IP ingress, required for metal cloud
|
||||
# TODO: is this the best option?
|
||||
${KOPS} edit cluster metal.k8s.local --set spec.api.publicName=10.123.45.10
|
||||
|
||||
# List clusters
|
||||
go run ./cmd/kops get cluster
|
||||
${KOPS} get cluster
|
||||
${KOPS} get cluster -oyaml
|
||||
|
||||
# List instance groups
|
||||
go run ./cmd/kops get ig --name metal.k8s.local
|
||||
go run ./cmd/kops get ig --name metal.k8s.local -oyaml
|
||||
${KOPS} get ig --name metal.k8s.local
|
||||
${KOPS} get ig --name metal.k8s.local -oyaml
|
||||
|
||||
# Apply basic configuration
|
||||
${KOPS} update cluster metal.k8s.local
|
||||
${KOPS} update cluster metal.k8s.local --yes --admin
|
||||
|
||||
echo "Test successful"
|
|
@ -146,6 +146,12 @@ func (s *S3Server) ServeRequest(ctx context.Context, w http.ResponseWriter, r *h
|
|||
key := strings.TrimPrefix(r.URL.Path, "/"+bucket+"/")
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
if values.Has("acl") {
|
||||
return s.GetObjectACL(ctx, req, &GetObjectACLInput{
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
})
|
||||
}
|
||||
return s.GetObject(ctx, req, &GetObjectInput{
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
|
@ -174,12 +180,15 @@ type ListObjectsV2Input struct {
|
|||
const s3TimeFormat = "2006-01-02T15:04:05.000Z"
|
||||
|
||||
func (s *S3Server) ListObjectsV2(ctx context.Context, req *s3Request, input *ListObjectsV2Input) error {
|
||||
bucket, err := s.store.GetBucket(ctx, input.Bucket)
|
||||
bucket, _, err := s.store.GetBucket(ctx, input.Bucket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get bucket %q: %w", input.Bucket, err)
|
||||
}
|
||||
if bucket == nil {
|
||||
return fmt.Errorf("bucket %q not found", input.Bucket)
|
||||
return req.writeError(ctx, http.StatusNotFound, &s3model.Error{
|
||||
Code: "NoSuchBucket",
|
||||
Message: "The specified bucket does not exist",
|
||||
})
|
||||
}
|
||||
|
||||
objects, err := bucket.ListObjects(ctx)
|
||||
|
@ -238,12 +247,15 @@ type GetObjectInput struct {
|
|||
}
|
||||
|
||||
func (s *S3Server) GetObject(ctx context.Context, req *s3Request, input *GetObjectInput) error {
|
||||
bucket, err := s.store.GetBucket(ctx, input.Bucket)
|
||||
bucket, _, err := s.store.GetBucket(ctx, input.Bucket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get bucket %q: %w", input.Bucket, err)
|
||||
}
|
||||
if bucket == nil {
|
||||
return req.writeError(ctx, http.StatusNotFound, nil)
|
||||
return req.writeError(ctx, http.StatusNotFound, &s3model.Error{
|
||||
Code: "NoSuchBucket",
|
||||
Message: "The specified bucket does not exist",
|
||||
})
|
||||
}
|
||||
|
||||
object, err := bucket.GetObject(ctx, input.Key)
|
||||
|
@ -261,6 +273,55 @@ func (s *S3Server) GetObject(ctx context.Context, req *s3Request, input *GetObje
|
|||
return object.WriteTo(req.w)
|
||||
}
|
||||
|
||||
type GetObjectACLInput struct {
|
||||
Bucket string
|
||||
Key string
|
||||
}
|
||||
|
||||
func (s *S3Server) GetObjectACL(ctx context.Context, req *s3Request, input *GetObjectACLInput) error {
|
||||
bucket, bucketInfo, err := s.store.GetBucket(ctx, input.Bucket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get bucket %q: %w", input.Bucket, err)
|
||||
}
|
||||
if bucket == nil {
|
||||
return req.writeError(ctx, http.StatusNotFound, &s3model.Error{
|
||||
Code: "NoSuchBucket",
|
||||
Message: "The specified bucket does not exist",
|
||||
})
|
||||
}
|
||||
|
||||
object, err := bucket.GetObject(ctx, input.Key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get object %q in bucket %q: %w", input.Key, input.Bucket, err)
|
||||
}
|
||||
|
||||
if object == nil {
|
||||
return req.writeError(ctx, http.StatusNotFound, &s3model.Error{
|
||||
Code: "NoSuchKey",
|
||||
Message: "The specified key does not exist.",
|
||||
})
|
||||
}
|
||||
|
||||
owner := bucketInfo.Owner
|
||||
|
||||
output := &s3model.ObjectACLResult{
|
||||
Owner: &s3model.Owner{
|
||||
ID: owner,
|
||||
},
|
||||
Grants: []*s3model.Grant{
|
||||
{
|
||||
Grantee: &s3model.Grantee{
|
||||
ID: owner,
|
||||
Type: "CanonicalUser",
|
||||
},
|
||||
Permission: "FULL_CONTROL",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return req.writeXML(ctx, output)
|
||||
}
|
||||
|
||||
type PutObjectInput struct {
|
||||
Bucket string
|
||||
Key string
|
||||
|
@ -269,12 +330,15 @@ type PutObjectInput struct {
|
|||
func (s *S3Server) PutObject(ctx context.Context, req *s3Request, input *PutObjectInput) error {
|
||||
log := klog.FromContext(ctx)
|
||||
|
||||
bucket, err := s.store.GetBucket(ctx, input.Bucket)
|
||||
bucket, _, err := s.store.GetBucket(ctx, input.Bucket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get bucket %q: %w", input.Bucket, err)
|
||||
}
|
||||
if bucket == nil {
|
||||
return req.writeError(ctx, http.StatusNotFound, nil)
|
||||
return req.writeError(ctx, http.StatusNotFound, &s3model.Error{
|
||||
Code: "NoSuchBucket",
|
||||
Message: "The specified bucket does not exist",
|
||||
})
|
||||
}
|
||||
|
||||
objectInfo, err := bucket.PutObject(ctx, input.Key, req.r.Body)
|
||||
|
|
|
@ -28,7 +28,7 @@ type ObjectStore interface {
|
|||
|
||||
// GetBucket returns the bucket with the given name.
|
||||
// If the bucket does not exist, it returns (nil, nil).
|
||||
GetBucket(ctx context.Context, name string) (Bucket, error)
|
||||
GetBucket(ctx context.Context, name string) (Bucket, *BucketInfo, error)
|
||||
|
||||
// CreateBucket creates the bucket with the given name.
|
||||
// If the bucket already exist, it returns codes.AlreadyExists.
|
||||
|
@ -38,6 +38,9 @@ type ObjectStore interface {
|
|||
type BucketInfo struct {
|
||||
Name string
|
||||
CreationDate time.Time
|
||||
|
||||
// Owner is the AWS ID of the bucket owner.
|
||||
Owner string
|
||||
}
|
||||
|
||||
type Bucket interface {
|
||||
|
|
|
@ -54,15 +54,21 @@ func (m *TestObjectStore) ListBuckets(ctx context.Context) []objectstore.BucketI
|
|||
return buckets
|
||||
}
|
||||
|
||||
func (m *TestObjectStore) GetBucket(ctx context.Context, bucketName string) (objectstore.Bucket, error) {
|
||||
func (m *TestObjectStore) GetBucket(ctx context.Context, bucketName string) (objectstore.Bucket, *objectstore.BucketInfo, error) {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
bucket := m.buckets[bucketName]
|
||||
if bucket == nil {
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
return bucket, nil
|
||||
return bucket, &bucket.info, nil
|
||||
}
|
||||
|
||||
// getOwnerID returns the owner ID for the given context.
|
||||
// This is a fake implementation for testing purposes.
|
||||
func getOwnerID(ctx context.Context) string {
|
||||
return "fake-owner"
|
||||
}
|
||||
|
||||
func (m *TestObjectStore) CreateBucket(ctx context.Context, bucketName string) (*objectstore.BucketInfo, error) {
|
||||
|
@ -83,7 +89,8 @@ func (m *TestObjectStore) CreateBucket(ctx context.Context, bucketName string) (
|
|||
bucket = &TestBucket{
|
||||
info: objectstore.BucketInfo{
|
||||
Name: bucketName,
|
||||
CreationDate: time.Now(),
|
||||
CreationDate: time.Now().UTC(),
|
||||
Owner: getOwnerID(ctx),
|
||||
},
|
||||
objects: make(map[string]*TestObject),
|
||||
}
|
||||
|
|
|
@ -67,3 +67,18 @@ type RestoreStatus struct {
|
|||
IsRestoreInProgress bool `xml:"IsRestoreInProgress"`
|
||||
RestoreExpiryDate *string `xml:"RestoreExpiryDate"`
|
||||
}
|
||||
|
||||
type ObjectACLResult struct {
|
||||
Owner *Owner `xml:"Owner"`
|
||||
Grants []*Grant `xml:"Grant"`
|
||||
}
|
||||
|
||||
type Grant struct {
|
||||
Grantee *Grantee `xml:"Grantee"`
|
||||
Permission string `xml:"Permission"`
|
||||
}
|
||||
|
||||
type Grantee struct {
|
||||
ID string `xml:"ID"`
|
||||
Type string `xml:"Type"`
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ import (
|
|||
"k8s.io/kops/upup/pkg/fi/cloudup/do"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/hetzner"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/metal"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
|
@ -722,6 +723,8 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) (*ApplyResults, error) {
|
|||
target = azure.NewAzureAPITarget(cloud.(azure.AzureCloud))
|
||||
case kops.CloudProviderScaleway:
|
||||
target = scaleway.NewScwAPITarget(cloud.(scaleway.ScwCloud))
|
||||
case kops.CloudProviderMetal:
|
||||
target = metal.NewAPITarget(cloud.(*metal.Cloud), nil)
|
||||
default:
|
||||
return nil, fmt.Errorf("direct configuration not supported with CloudProvider:%q", cluster.GetCloudProvider())
|
||||
}
|
||||
|
|
|
@ -18,8 +18,10 @@ package metal
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kops/dnsprovider/pkg/dnsprovider"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/cloudinstances"
|
||||
|
@ -84,9 +86,31 @@ func (c *Cloud) Region() string {
|
|||
|
||||
// FindClusterStatus discovers the status of the cluster, by inspecting the cloud objects
|
||||
func (c *Cloud) FindClusterStatus(cluster *kops.Cluster) (*kops.ClusterStatus, error) {
|
||||
return nil, fmt.Errorf("method metal.Cloud::FindClusterStatus not implemented")
|
||||
// etcdStatus, err := findEtcdStatus(c, cluster)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
klog.Warningf("method metal.Cloud::FindClusterStatus stub-implemented")
|
||||
return &kops.ClusterStatus{
|
||||
// EtcdClusters: etcdStatus,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Cloud) GetApiIngressStatus(cluster *kops.Cluster) ([]fi.ApiIngressStatus, error) {
|
||||
return nil, fmt.Errorf("method metal.Cloud::GetApiIngressStatus not implemented")
|
||||
var ret []fi.ApiIngressStatus
|
||||
publicName := cluster.Spec.API.PublicName
|
||||
if publicName == "" {
|
||||
return ret, fmt.Errorf("spec.api.publicName must be set for bare metal")
|
||||
}
|
||||
ip := net.ParseIP(publicName)
|
||||
if ip == nil {
|
||||
ret = append(ret, fi.ApiIngressStatus{
|
||||
Hostname: publicName,
|
||||
})
|
||||
} else {
|
||||
ret = append(ret, fi.ApiIngressStatus{
|
||||
IP: publicName,
|
||||
})
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue