Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

metal: more functions to enable kops update cluster #16789

Merged
merged 1 commit into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions tests/e2e/scenarios/bare-metal/run-test
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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"
76 changes: 70 additions & 6 deletions tools/metal/storage/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion tools/metal/storage/pkg/objectstore/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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),
}
Expand Down
15 changes: 15 additions & 0 deletions tools/metal/storage/pkg/s3model/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
3 changes: 3 additions & 0 deletions upup/pkg/fi/cloudup/apply_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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())
}
Expand Down
28 changes: 26 additions & 2 deletions upup/pkg/fi/cloudup/metal/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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