Skip to content

Commit

Permalink
GCE Dump: Include instance IPs
Browse files Browse the repository at this point in the history
The challenge here is that we normally only get the instance url.  So we
have to do another call to GCE, but we also don't want to do one call
per instance.

Instead, we create a dump operation context object which we pass into the dump.
  • Loading branch information
justinsb committed Oct 30, 2017
1 parent b02c3a2 commit b3e00c0
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 29 deletions.
3 changes: 2 additions & 1 deletion cmd/kops/toolbox_dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package main

import (
"context"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -112,7 +113,7 @@ func RunToolboxDump(f *util.Factory, out io.Writer, options *ToolboxDumpOptions)
if err != nil {
return err
}
dump, err := resources.BuildDump(resourceMap)
dump, err := resources.BuildDump(context.TODO(), cloud, resourceMap)
if err != nil {
return err
}
Expand Down
22 changes: 11 additions & 11 deletions pkg/resources/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,12 +314,12 @@ func DeleteCloudFormationStack(cloud fi.Cloud, t *Resource) error {
return nil
}

func DumpCloudFormationStack(r *Resource, dump *Dump) error {
func DumpCloudFormationStack(op *DumpOperation, r *Resource) error {
data := make(map[string]interface{})
data["id"] = r.ID
data["type"] = r.Type
data["raw"] = r.Obj
dump.Resources = append(dump.Resources, data)
op.Dump.Resources = append(op.Dump.Resources, data)
return nil
}

Expand Down Expand Up @@ -417,12 +417,12 @@ func ListInstances(cloud fi.Cloud, clusterName string) ([]*Resource, error) {
return resourceTrackers, nil
}

func DumpInstance(r *Resource, dump *Dump) error {
func DumpInstance(op *DumpOperation, r *Resource) error {
data := make(map[string]interface{})
data["id"] = r.ID
data["type"] = ec2.ResourceTypeInstance
data["raw"] = r.Obj
dump.Resources = append(dump.Resources, data)
op.Dump.Resources = append(op.Dump.Resources, data)

ec2Instance := r.Obj.(*ec2.Instance)
i := &Instance{
Expand All @@ -436,7 +436,7 @@ func DumpInstance(r *Resource, dump *Dump) error {
}
}
}
dump.Instances = append(dump.Instances, i)
op.Dump.Instances = append(op.Dump.Instances, i)

return nil
}
Expand Down Expand Up @@ -496,12 +496,12 @@ func DeleteSecurityGroup(cloud fi.Cloud, t *Resource) error {
return nil
}

func DumpSecurityGroup(r *Resource, dump *Dump) error {
func DumpSecurityGroup(op *DumpOperation, r *Resource) error {
data := make(map[string]interface{})
data["id"] = r.ID
data["type"] = ec2.ResourceTypeSecurityGroup
data["raw"] = r.Obj
dump.Resources = append(dump.Resources, data)
op.Dump.Resources = append(op.Dump.Resources, data)
return nil
}

Expand Down Expand Up @@ -1218,12 +1218,12 @@ func DeleteVPC(cloud fi.Cloud, r *Resource) error {
return nil
}

func DumpVPC(r *Resource, dump *Dump) error {
func DumpVPC(op *DumpOperation, r *Resource) error {
data := make(map[string]interface{})
data["id"] = r.ID
data["type"] = ec2.ResourceTypeVpc
data["raw"] = r.Obj
dump.Resources = append(dump.Resources, data)
op.Dump.Resources = append(op.Dump.Resources, data)
return nil
}

Expand Down Expand Up @@ -1558,12 +1558,12 @@ func DeleteELB(cloud fi.Cloud, r *Resource) error {
return nil
}

func DumpELB(r *Resource, dump *Dump) error {
func DumpELB(op *DumpOperation, r *Resource) error {
data := make(map[string]interface{})
data["id"] = r.ID
data["type"] = TypeLoadBalancer
data["raw"] = r.Obj
dump.Resources = append(dump.Resources, data)
op.Dump.Resources = append(op.Dump.Resources, data)
return nil
}

Expand Down
27 changes: 22 additions & 5 deletions pkg/resources/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,44 @@ limitations under the License.
package resources

import (
"context"
"fmt"

"github.com/golang/glog"
"k8s.io/kops/upup/pkg/fi"
)

// Dumpable is the interface that Resources that can report into the dump should implement
type Dumpable interface {
Dump(dump *Dump) error
// DumpOperation holds context information for a dump, allowing for extension
type DumpOperation struct {
// Context is the golang context.Context for the dump operation
Context context.Context

// Cloud is the cloud we are dumping
Cloud fi.Cloud

// CloudState allows the cloudprovider to store state during the dump operation
CloudState interface{}

// Dump is the target of our dump
Dump *Dump
}

// BuildDump gathers information about the cluster and returns an object for dumping
func BuildDump(resources map[string]*Resource) (*Dump, error) {
func BuildDump(ctx context.Context, cloud fi.Cloud, resources map[string]*Resource) (*Dump, error) {
dump := &Dump{}
op := &DumpOperation{
Context: ctx,
Cloud: cloud,
Dump: dump,
}

for k, r := range resources {
if r.Dumper == nil {
glog.V(8).Infof("skipping dump of %q (does not implement Dumpable)", k)
continue
}

err := r.Dumper(r, dump)
err := r.Dumper(op, r)
if err != nil {
return nil, fmt.Errorf("error dumping %q: %v", k, err)
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/resources/gce/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = ["gce.go"],
srcs = [
"dump.go",
"gce.go",
],
visibility = ["//visibility:public"],
deps = [
"//pkg/resources:go_default_library",
Expand Down
118 changes: 118 additions & 0 deletions pkg/resources/gce/dump.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
Copyright 2017 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 gce

import (
"context"
"fmt"
"sync"

"github.com/golang/glog"
compute "google.golang.org/api/compute/v0.beta"
"k8s.io/kops/pkg/resources"
gce "k8s.io/kops/upup/pkg/fi/cloudup/gce"
)

// dumpState holds state for use during a GCE dump operation
type dumpState struct {
// cloud is the a reference to the GCE cloud we are dumping
cloud gce.GCECloud

// mutex protects the follow resources
mutex sync.Mutex

// instances is a cache of instances by zone
instances map[string]map[string]*compute.Instance
}

// DumpManagedInstance is responsible for dumping a resource for a ManagedInstance
func DumpManagedInstance(op *resources.DumpOperation, r *resources.Resource) error {
instance := r.Obj.(*compute.ManagedInstance)

u, err := gce.ParseGoogleCloudURL(instance.Instance)
if err != nil {
return fmt.Errorf("unable to parse instance url %q", instance.Instance)
}

// Fetch instance details
instanceMap, err := getDumpState(op).getInstances(op.Context, u.Zone)
if err != nil {
return err
}

i := &resources.Instance{
Name: u.Name,
}

instanceDetails := instanceMap[u.Name]
if instanceDetails == nil {
glog.Warningf("instance %q not found", instance.Instance)
} else {
for _, ni := range instanceDetails.NetworkInterfaces {
for _, ac := range ni.AccessConfigs {
if ac.NatIP != "" {
i.PublicAddresses = append(i.PublicAddresses, ac.NatIP)
}
}
}
}

op.Dump.Instances = append(op.Dump.Instances, i)

// Unclear if we should include the instance details in the dump - assume YAGNI until someone needs it
//dump.Resources = append(dump.Resources, instanceDetails)

return nil
}

// getDumpState gets the dumpState from the dump context, or creates one if not yet initialized
func getDumpState(dumpContext *resources.DumpOperation) *dumpState {
if dumpContext.CloudState == nil {
dumpContext.CloudState = &dumpState{
cloud: dumpContext.Cloud.(gce.GCECloud),
}
}
return dumpContext.CloudState.(*dumpState)
}

// getInstances retrieves the list of instances from the cloud, using a cached copy if possible
func (s *dumpState) getInstances(ctx context.Context, zone string) (map[string]*compute.Instance, error) {
s.mutex.Lock()
defer s.mutex.Unlock()

if s.instances == nil {
s.instances = make(map[string]map[string]*compute.Instance)
}

if s.instances[zone] != nil {
return s.instances[zone], nil
}

instances := make(map[string]*compute.Instance)
err := s.cloud.Compute().Instances.List(s.cloud.Project(), zone).Pages(ctx, func(page *compute.InstanceList) error {
for _, i := range page.Items {
instances[i.Name] = i
}
return nil
})
if err != nil {
return nil, err
}

s.instances[zone] = instances
return instances, nil
}
10 changes: 2 additions & 8 deletions pkg/resources/gce/gce.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,8 @@ func (d *clusterDiscoveryGCE) listManagedInstances(igm *compute.InstanceGroupMan
Deleter: func(cloud fi.Cloud, tracker *resources.Resource) error {
return gce.DeleteInstance(c, url)
},
Dumper: func(r *resources.Resource, dump *resources.Dump) error {
i := &resources.Instance{
Name: name,
}
dump.Instances = append(dump.Instances, i)
return nil
},
Obj: i.Instance,
Dumper: DumpManagedInstance,
Obj: i,
}

// We don't block deletion of the instance group manager
Expand Down
2 changes: 1 addition & 1 deletion pkg/resources/tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type Resource struct {
GroupDeleter func(cloud fi.Cloud, trackers []*Resource) error

// Dumper populates the dump with any information from the resource
Dumper func(r *Resource, dump *Dump) error
Dumper func(op *DumpOperation, r *Resource) error

Obj interface{}
}
4 changes: 2 additions & 2 deletions pkg/resources/vsphere.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,12 @@ func deleteVM(cloud fi.Cloud, r *Resource) error {
return nil
}

func DumpVMInfo(r *Resource, dump *Dump) error {
func DumpVMInfo(op *DumpOperation, r *Resource) error {
data := make(map[string]interface{})
data["id"] = r.ID
data["type"] = r.Type
data["raw"] = r.Obj
dump.Resources = append(dump.Resources, data)
op.Dump.Resources = append(op.Dump.Resources, data)
return nil
}

Expand Down

0 comments on commit b3e00c0

Please sign in to comment.