Skip to content

Commit

Permalink
GCE: Support kops-controller, including in gossip mode
Browse files Browse the repository at this point in the history
We discover the kops-controller in gossip mode using seeding code that
calls into the GCE API, just like gossip itself does.

We refactor the gossip code into a shared gcediscovery library with
minimal dependencies.
  • Loading branch information
justinsb committed Dec 4, 2021
1 parent 3c0e7a4 commit 4cf52d0
Show file tree
Hide file tree
Showing 24 changed files with 491 additions and 192 deletions.
6 changes: 5 additions & 1 deletion nodeup/pkg/model/bootstrap_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ func (b BootstrapClientBuilder) Build(c *fi.ModelBuilderContext) error {
authenticator, err = awsup.NewAWSAuthenticator(b.Cloud.Region())
case kops.CloudProviderGCE:
authenticator, err = gcetpmsigner.NewTPMAuthenticator()
// We don't use the custom resolver here in gossip mode (though we could);
// instead we use this as a check that protokube has now started.

default:
return fmt.Errorf("unsupported cloud provider %s", b.Cluster.Spec.CloudProvider)
return fmt.Errorf("unsupported cloud provider for authenticator %q", b.Cluster.Spec.CloudProvider)
}

if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions pkg/model/components/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ func Image(component string, clusterSpec *kops.ClusterSpec, assetsBuilder *asset
return image, nil
}

// GCETagForRole returns the (network) tag for GCE instances in the given instance group role.
func GCETagForRole(clusterName string, role kops.InstanceGroupRole) string {
return gce.SafeClusterName(clusterName) + "-" + gce.GceLabelNameRolePrefix + strings.ToLower(string(role))
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/model/components/kubelet.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
"k8s.io/kops/upup/pkg/fi/loader"
)

Expand Down Expand Up @@ -141,7 +142,7 @@ func (b *KubeletOptionsBuilder) BuildOptions(o interface{}) error {
clusterSpec.CloudConfig = &kops.CloudConfiguration{}
}
clusterSpec.CloudConfig.Multizone = fi.Bool(true)
clusterSpec.CloudConfig.NodeTags = fi.String(GCETagForRole(b.ClusterName, kops.InstanceGroupRoleNode))
clusterSpec.CloudConfig.NodeTags = fi.String(gce.TagForRole(b.ClusterName, kops.InstanceGroupRoleNode))

}

Expand Down
2 changes: 1 addition & 1 deletion pkg/model/gcemodel/BUILD.bazel

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pkg/model/gcemodel/autoscalinggroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
nodeidentitygce "k8s.io/kops/pkg/nodeidentity/gce"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
"k8s.io/kops/upup/pkg/fi/cloudup/gce/gcemetadata"
"k8s.io/kops/upup/pkg/fi/cloudup/gcetasks"
)

Expand Down Expand Up @@ -97,7 +98,7 @@ func (b *AutoscalingGroupModelBuilder) buildInstanceTemplate(c *fi.ModelBuilderC
Metadata: map[string]fi.Resource{
"startup-script": startupScript,
//"config": resources/config.yaml $nodeset.Name
"cluster-name": fi.NewStringResource(b.ClusterName()),
gcemetadata.MetadataKeyClusterName: fi.NewStringResource(b.ClusterName()),
nodeidentitygce.MetadataKeyInstanceGroupName: fi.NewStringResource(ig.Name),
},
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/model/gcemodel/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package gcemodel
import (
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/model"
"k8s.io/kops/pkg/model/components"
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
"k8s.io/kops/upup/pkg/fi/cloudup/gcetasks"
)
Expand Down Expand Up @@ -67,8 +66,9 @@ func (c *GCEModelContext) SafeClusterName() string {
return gce.SafeClusterName(c.Cluster.ObjectMeta.Name)
}

// GCETagForRole returns the (network) tag for GCE instances in the given instance group role.
func (c *GCEModelContext) GCETagForRole(role kops.InstanceGroupRole) string {
return components.GCETagForRole(c.Cluster.ObjectMeta.Name, role)
return gce.TagForRole(c.Cluster.ObjectMeta.Name, role)
}

func (c *GCEModelContext) LinkToTargetPool(id string) *gcetasks.TargetPool {
Expand Down
8 changes: 8 additions & 0 deletions pkg/resolver/BUILD.bazel

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions pkg/resolver/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
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 resolver

import "context"

// Resolver is implemented by alternatives resolvers (such as gossip-mode)
type Resolver interface {
// Resolve resolves the host to IP addresses or alternative hostnames.
Resolve(ctx context.Context, host string) ([]string, error)
}
115 changes: 0 additions & 115 deletions protokube/pkg/gossip/gce/seeds.go

This file was deleted.

2 changes: 1 addition & 1 deletion protokube/pkg/protokube/BUILD.bazel

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 12 additions & 41 deletions protokube/pkg/protokube/gce_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ import (
"k8s.io/klog/v2"
"k8s.io/kops/protokube/pkg/etcd"
"k8s.io/kops/protokube/pkg/gossip"
gossipgce "k8s.io/kops/protokube/pkg/gossip/gce"
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
"k8s.io/kops/upup/pkg/fi/cloudup/gce/gcediscovery"
)

// GCEVolumes is the Volumes implementation for GCE
type GCEVolumes struct {
compute *compute.Service
compute *compute.Service
discovery *gcediscovery.Discovery

project string
zone string
Expand All @@ -48,15 +49,14 @@ var _ Volumes = &GCEVolumes{}

// NewGCEVolumes builds a GCEVolumes
func NewGCEVolumes() (*GCEVolumes, error) {
ctx := context.Background()

computeService, err := compute.NewService(ctx)
discovery, err := gcediscovery.New()
if err != nil {
return nil, fmt.Errorf("error building compute API client: %v", err)
return nil, err
}

a := &GCEVolumes{
compute: computeService,
discovery: discovery,
compute: discovery.Compute(),
}

err = a.discoverTags()
Expand Down Expand Up @@ -85,24 +85,15 @@ func (a *GCEVolumes) InternalIP() net.IP {
func (a *GCEVolumes) discoverTags() error {
// Cluster Name
{
clusterName, err := metadata.InstanceAttributeValue("cluster-name")
if err != nil {
return fmt.Errorf("error reading cluster-name attribute from GCE: %v", err)
}
a.clusterName = strings.TrimSpace(string(clusterName))
a.clusterName = a.discovery.ClusterName()
if a.clusterName == "" {
return fmt.Errorf("cluster-name metadata was empty")
}
klog.Infof("Found cluster-name=%q", a.clusterName)
}

// Project ID
{
project, err := metadata.ProjectID()
if err != nil {
return fmt.Errorf("error reading project from GCE: %v", err)
}
a.project = strings.TrimSpace(project)
a.project = a.discovery.ProjectID()
if a.project == "" {
return fmt.Errorf("project metadata was empty")
}
Expand All @@ -111,21 +102,13 @@ func (a *GCEVolumes) discoverTags() error {

// Zone
{
zone, err := metadata.Zone()
if err != nil {
return fmt.Errorf("error reading zone from GCE: %v", err)
}
a.zone = strings.TrimSpace(zone)
a.zone = a.discovery.Zone()
if a.zone == "" {
return fmt.Errorf("zone metadata was empty")
}
klog.Infof("Found zone=%q", a.zone)

region, err := regionFromZone(zone)
if err != nil {
return fmt.Errorf("error determining region from zone %q: %v", zone, err)
}
a.region = region
a.region = a.discovery.Region()
klog.Infof("Found region=%q", a.region)
}

Expand Down Expand Up @@ -359,21 +342,9 @@ func (v *GCEVolumes) AttachVolume(volume *Volume) error {
}

func (g *GCEVolumes) GossipSeeds() (gossip.SeedProvider, error) {
return gossipgce.NewSeedProvider(g.compute, g.region, g.project)
return g.discovery, nil
}

func (g *GCEVolumes) InstanceName() string {
return g.instanceName
}

// regionFromZone returns region of the gce zone. Zone names
// are of the form: ${region-name}-${ix}.
// For example, "us-central1-b" has a region of "us-central1".
// So we look for the last '-' and trim to just before that.
func regionFromZone(zone string) (string, error) {
ix := strings.LastIndex(zone, "-")
if ix == -1 {
return "", fmt.Errorf("unexpected zone: %s", zone)
}
return zone[:ix], nil
}
1 change: 1 addition & 0 deletions upup/pkg/fi/cloudup/gce/BUILD.bazel

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4cf52d0

Please sign in to comment.