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

get ip from control plan during cluster reconciliation #32

Merged
merged 2 commits into from
May 7, 2020
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
5 changes: 5 additions & 0 deletions api/v1alpha3/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ type PacketResourceStatus string
var (
// PacketResourceStatus is the string representing a Packet resource just created and in a provisioning state.
PacketResourceStatusNew = PacketResourceStatus("new")
// PacketResourceStatusQueued is the string representing a Packet resource that is waiting in a queue to be created.
PacketResourceStatusQueued = PacketResourceStatus("queued")
// PacketResourceStatusQueued is the string representing a Packet resource
// that got picked from a worker that is not provisioning it.
PacketResourceStatusProvisioning = PacketResourceStatus("provisioning")
// PacketResourceStatusRunning is the string representing a Packet resource already provisioned and in a active state.
PacketResourceStatusRunning = PacketResourceStatus("active")
// PacketResourceStatusErrored is the string representing a Packet resource in a errored state.
Expand Down
74 changes: 70 additions & 4 deletions controllers/packetcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package controllers

import (
"context"
"fmt"
"time"

"github.com/go-logr/logr"
Expand All @@ -33,15 +34,17 @@ import (
"sigs.k8s.io/controller-runtime/pkg/source"

infrastructurev1alpha3 "github.com/packethost/cluster-api-provider-packet/api/v1alpha3"
packet "github.com/packethost/cluster-api-provider-packet/pkg/cloud/packet"
"github.com/packethost/cluster-api-provider-packet/pkg/cloud/packet/scope"
)

// PacketClusterReconciler reconciles a PacketCluster object
type PacketClusterReconciler struct {
client.Client
Log logr.Logger
Recorder record.EventRecorder
Scheme *runtime.Scheme
Log logr.Logger
Recorder record.EventRecorder
Scheme *runtime.Scheme
PacketClient *packet.PacketClient
}

// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=packetclusters,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -94,9 +97,30 @@ func (r *PacketClusterReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, re
}
}()

// we have no setup to be done
clusterScope.PacketCluster.Status.Ready = true

address, err := r.getIP(clusterScope.PacketCluster)
_, isNoMachine := err.(*MachineNotFound)
_, isNoIP := err.(*MachineNoIP)
switch {
case err != nil && isNoMachine:
logger.Info("Control plan device not found. Requeueing...")
return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, nil
case err != nil && isNoIP:
logger.Info("Control plan device not found. Requeueing...")
return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, nil
case err != nil:
logger.Error(err, "error getting a control plan ip")
return ctrl.Result{}, err
case err == nil:
clusterScope.PacketCluster.Status.APIEndpoints = []infrastructurev1alpha3.APIEndpoint{
{
Host: address,
Port: 6443,
},
}
}

return ctrl.Result{}, nil
}

Expand All @@ -111,3 +135,45 @@ func (r *PacketClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
).
Complete(r)
}

func (r *PacketClusterReconciler) getIP(cluster *infrastructurev1alpha3.PacketCluster) (string, error) {
if cluster == nil {
return "", fmt.Errorf("cannot get IP of machine in nil cluster")
}
tags := []string{
packet.GenerateClusterTag(string(cluster.Name)),
infrastructurev1alpha3.MasterTag,
}
device, err := r.PacketClient.GetDeviceByTags(cluster.Spec.ProjectID, tags)
if err != nil {
return "", fmt.Errorf("error retrieving machine: %v", err)
}
if device == nil {
return "", &MachineNotFound{err: fmt.Sprintf("machine does not exist")}
}
if device.Network == nil || len(device.Network) == 0 || device.Network[0].Address == "" {
return "", &MachineNoIP{err: "machine does not yet have an IP address"}
}
// TODO: validate that this address exists, so we don't hit nil pointer
// TODO: check which address to return
// TODO: check address format (cidr, subnet, etc.)
return device.Network[0].Address, nil
}

// MachineNotFound error representing that the requested device was not yet found
type MachineNotFound struct {
err string
}

func (e *MachineNotFound) Error() string {
return e.err
}

// MachineNoIP error representing that the requested device does not have an IP yet assigned
type MachineNoIP struct {
err string
}

func (e *MachineNoIP) Error() string {
return e.err
}
2 changes: 1 addition & 1 deletion controllers/packetmachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func (r *PacketMachineReconciler) reconcile(ctx context.Context, machineScope *s
var result = ctrl.Result{}

switch infrastructurev1alpha3.PacketResourceStatus(dev.State) {
case infrastructurev1alpha3.PacketResourceStatusNew:
case infrastructurev1alpha3.PacketResourceStatusNew, infrastructurev1alpha3.PacketResourceStatusQueued:
machineScope.Info("Machine instance is pending", "instance-id", machineScope.GetInstanceID())
result = ctrl.Result{RequeueAfter: 10 * time.Second}
case infrastructurev1alpha3.PacketResourceStatusRunning:
Expand Down
9 changes: 5 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,11 @@ func main() {
}

if err = (&controllers.PacketClusterReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("PacketCluster"),
Recorder: mgr.GetEventRecorderFor("packetcluster-controller"),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("PacketCluster"),
Recorder: mgr.GetEventRecorderFor("packetcluster-controller"),
PacketClient: client,
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "PacketCluster")
os.Exit(1)
Expand Down
14 changes: 14 additions & 0 deletions pkg/cloud/packet/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,17 @@ func (p *PacketClient) GetDeviceAddresses(device *packngo.Device) ([]corev1.Node
}
return addrs, nil
}

func (p *PacketClient) GetDeviceByTags(project string, tags []string) (*packngo.Device, error) {
devices, _, err := p.Devices.List(project, nil)
if err != nil {
return nil, fmt.Errorf("Error retrieving devices: %v", err)
}
// returns the first one that matches all of the tags
for _, device := range devices {
if ItemsInList(device.Tags, tags) {
return &device, nil
}
}
return nil, nil
}
30 changes: 27 additions & 3 deletions pkg/cloud/packet/util.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
package packet

import "fmt"
import (
"fmt"
)

const (
machineUIDTag = "cluster-api-provider-packet:machine-uid"
MachineUIDTag = "cluster-api-provider-packet:machine-uid"
clusterIDTag = "cluster-api-provider-packet:cluster-id"
AnnotationUID = "cluster.k8s.io/machine-uid"
)

func GenerateMachineTag(ID string) string {
return fmt.Sprintf("%s:%s", machineUIDTag, ID)
return fmt.Sprintf("%s:%s", MachineUIDTag, ID)
}
func GenerateClusterTag(ID string) string {
return fmt.Sprintf("%s:%s", clusterIDTag, ID)
}

// ItemsInList checks if all items are in the list
func ItemsInList(list []string, items []string) bool {
// convert the items against which we are mapping into a map
itemMap := map[string]bool{}
for _, elm := range items {
itemMap[elm] = false
}
// every one that is matched goes from false to true in the map
for _, elm := range list {
if _, ok := itemMap[elm]; ok {
itemMap[elm] = true
}
}
// go through the map; if any is false, return false, else all matched so return true
for _, v := range itemMap {
if !v {
return false
}
}
return true
}