Skip to content

Commit

Permalink
Add initial controllers implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Mateusz Gozdek <mateusz@kinvolk.io>
  • Loading branch information
invidian committed Dec 18, 2020
1 parent d8e1343 commit 7f66656
Show file tree
Hide file tree
Showing 20 changed files with 1,744 additions and 152 deletions.
18 changes: 8 additions & 10 deletions api/v1alpha3/tinkerbellcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1alpha3

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
Expand All @@ -27,16 +28,13 @@ import (
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file.
type TinkerbellClusterSpec struct {
// HardwareDiscoveryStrategy is a switch we have to implement more advacned
// discovery strategy. The unique one we have today is the default one
// obviously and it uses the two lists of hardware IDs specified down here.
HardwareDiscoveryStrategy string `json:"hardwareDiscoveryStrategy,omitempty"`
// ControlPlaneHardwareIDs contains a list of hardware IDs used as pool for
// control plane kubernetes instances.
ControlPlaneHardwareIDs []string `json:"controlPlaneHardwareIDs,omitempty"`
// MachineHardwareIDs contains a list of hardware IDs used as pool for data
// plane kubernetes instances.
MachineHardwareIDs []string `json:"machineHardwareIDs,omitempty"`
// ControlPlaneEndpoint is a required field by ClusterAPI v1alpha3.
//
// See https://cluster-api.sigs.k8s.io/developer/architecture/controllers/cluster.html
// for more details.
//
// +optional
ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint,omitempty"`
}

// TinkerbellClusterStatus defines the observed state of TinkerbellCluster.
Expand Down
10 changes: 9 additions & 1 deletion api/v1alpha3/tinkerbellmachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ const (

// TinkerbellMachineSpec defines the desired state of TinkerbellMachine.
type TinkerbellMachineSpec struct {
HardwareID string `json:"hardwareReservationID,omitempty"`
// List of SSH public keys to allow root user to log in as.
SSHPublicKeys []string `json:"sshPublicKeys,omitempty"`

// TODO: Those fields are not intended to be filled in by the user, but by the controller.
// Should we move them to Status struct?
HardwareID string `json:"hardwareID,omitempty"`
TemplateID string `json:"templateID,omitempty"`
WorkflowID string `json:"workflowID,omitempty"`
ProviderID string `json:"providerID,omitempty"`
}

// TinkerbellMachineStatus defines the observed state of TinkerbellMachine.
Expand Down
26 changes: 11 additions & 15 deletions api/v1alpha3/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,22 @@ spec:
INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important:
Run "make" to regenerate code after modifying this file.'
properties:
controlPlaneHardwareIDs:
description: ControlPlaneHardwareIDs contains a list of hardware IDs
used as pool for control plane kubernetes instances.
items:
type: string
type: array
hardwareDiscoveryStrategy:
description: HardwareDiscoveryStrategy is a switch we have to implement
more advacned discovery strategy. The unique one we have today is
the default one obviously and it uses the two lists of hardware
IDs specified down here.
type: string
machineHardwareIDs:
description: MachineHardwareIDs contains a list of hardware IDs used
as pool for data plane kubernetes instances.
items:
type: string
type: array
controlPlaneEndpoint:
description: "ControlPlaneEndpoint is a required field by ClusterAPI
v1alpha3. \n See https://cluster-api.sigs.k8s.io/developer/architecture/controllers/cluster.html
for more details."
properties:
host:
description: The hostname on which the API server is serving.
type: string
port:
description: The port on which the API server is serving.
format: int32
type: integer
required:
- host
- port
type: object
type: object
status:
description: TinkerbellClusterStatus defines the observed state of TinkerbellCluster.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,22 @@ spec:
spec:
description: TinkerbellMachineSpec defines the desired state of TinkerbellMachine.
properties:
hardwareReservationID:
hardwareID:
description: 'TODO: Those fields are not intended to be filled in
by the user, but by the controller. Should we move them to Status
struct?'
type: string
providerID:
type: string
sshPublicKeys:
description: List of SSH public keys to allow root user to log in
as.
items:
type: string
type: array
templateID:
type: string
workflowID:
type: string
type: object
status:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,22 @@ spec:
description: Spec is the specification of the desired behavior
of the machine.
properties:
hardwareReservationID:
hardwareID:
description: 'TODO: Those fields are not intended to be filled
in by the user, but by the controller. Should we move them
to Status struct?'
type: string
providerID:
type: string
sshPublicKeys:
description: List of SSH public keys to allow root user to
log in as.
items:
type: string
type: array
templateID:
type: string
workflowID:
type: string
type: object
required:
Expand Down
91 changes: 16 additions & 75 deletions controllers/tinkerbellcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,93 +18,52 @@ limitations under the License.
package controllers

import (
"context"
"fmt"
"time"

"github.com/go-logr/logr"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/record"
"k8s.io/apimachinery/pkg/types"
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
"sigs.k8s.io/cluster-api/util"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/source"

infrastructurev1alpha3 "github.com/tinkerbell/cluster-api-provider-tinkerbell/api/v1alpha3"
"github.com/tinkerbell/cluster-api-provider-tinkerbell/internal/reconcilers"
)

// TinkerbellClusterReconciler implements Reconciler interface.
type TinkerbellClusterReconciler struct {
client.Client
Log logr.Logger
Recorder record.EventRecorder
Scheme *runtime.Scheme
Log logr.Logger
ClusterReconcileContextConfig *reconcilers.ClusterReconcileContextConfig
}

// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=tinkerbellclusters,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=tinkerbellclusters/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;clusters/status,verbs=get;list;watch

// Reconcile ensures state of Tinkerbell clusters.
func (r *TinkerbellClusterReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, reterr error) {
ctx := context.Background()
logger := r.Log.WithValues("tinkerbellcluster", req.NamespacedName)

// Your logic here.
tcluster := &infrastructurev1alpha3.TinkerbellCluster{}
if err := r.Get(ctx, req.NamespacedName, tcluster); err != nil {
if apierrors.IsNotFound(err) {
return ctrl.Result{}, nil
}

return ctrl.Result{}, fmt.Errorf("getting cluster: %w", err)
func (r *TinkerbellClusterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
if err := r.reconcile(req.NamespacedName); err != nil {
return ctrl.Result{}, fmt.Errorf("reconciling: %w", err)
}

logger = logger.WithName(tcluster.APIVersion)
return ctrl.Result{}, nil
}

// Fetch the Machine.
cluster, err := util.GetOwnerCluster(ctx, r.Client, tcluster.ObjectMeta)
func (r *TinkerbellClusterReconciler) reconcile(namespacedName types.NamespacedName) error {
crc, err := r.ClusterReconcileContextConfig.New(namespacedName)
if err != nil {
return ctrl.Result{}, fmt.Errorf("getting owner cluster: %w", err)
}

if cluster == nil {
logger.Info("OwnerCluster is not set yet. Requeuing...")

return ctrl.Result{
Requeue: true,
RequeueAfter: 2 * time.Second, //nolint:gomnd
}, nil
return fmt.Errorf("creating reconciliation context: %w", err)
}

if util.IsPaused(cluster, tcluster) {
logger.Info("TinkerbellCluster or linked Cluster is marked as paused. Won't reconcile")
if crc == nil {
r.Log.Info("reconciliation dependencies are not ready yet")

return ctrl.Result{}, nil
return nil
}

// Handle deleted clusters.
if !cluster.DeletionTimestamp.IsZero() {
return r.reconcileDelete()
}

return r.reconcileNormal(tcluster)
}

//nolint:lll
func (r *TinkerbellClusterReconciler) reconcileNormal(tcluster *infrastructurev1alpha3.TinkerbellCluster) (ctrl.Result, error) {
return ctrl.Result{}, nil
}

func (r *TinkerbellClusterReconciler) reconcileDelete() (ctrl.Result, error) {
// Initially I created this handler to remove an elastic IP when a cluster
// gets delete, but it does not sound like a good idea. It is better to
// leave to the users the ability to decide if they want to keep and resign
// the IP or if they do not need it anymore.
return ctrl.Result{}, nil
return crc.Reconcile()
}

// SetupWithManager configures reconciler with a given manager.
Expand All @@ -119,21 +78,3 @@ func (r *TinkerbellClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
).
Complete(r)
}

// 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
}
Loading

0 comments on commit 7f66656

Please sign in to comment.