Skip to content

Commit

Permalink
Merge pull request #45 from kotaicode/generic-monitors
Browse files Browse the repository at this point in the history
Generic monitors
  • Loading branch information
Tomjosetj31 authored Feb 10, 2023
2 parents d1e6d99 + e73bde5 commit 110629c
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 49 deletions.
19 changes: 19 additions & 0 deletions clients/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ type CloudResource interface {
Status() (ResourceStatus, error)
}

type ResourceMonitor interface {
GetNewResources(clusterResources map[string]bool) ([]string, error)
}

var kubeconfig string

func init() {
Expand Down Expand Up @@ -68,6 +72,21 @@ func ResourceFactory(resType, tag string) (CloudResource, error) {
return resource, nil
}

// MonitorFactory generates structs that abide by the ResourceMonitor interface.
// The returned struct can get new resources of the specified type. Each new integration needso to be added to this factory function.
func MonitorFactory(monitorType string) (ResourceMonitor, error) {
var resourceMonitor ResourceMonitor

switch monitorType {
case TypeEC2:
resourceMonitor = &EC2Monitor{Type: monitorType}
default:
return nil, errors.New("Monitor type not found")
}

return resourceMonitor, nil
}

// GetClient returns a ready to use kubernetes client.
func GetClient() (client.Client, error) {
var err error
Expand Down
38 changes: 30 additions & 8 deletions clients/ec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const (
resourceMonitorTagKey string = "resource-booking/managed"
)

type EC2Monitor struct {
Type string
}

// Resource represents a collection of EC2 instances grouped by a common "resource-booking/application" tag.
type EC2Resource struct {
NameTag string
Expand Down Expand Up @@ -124,11 +128,22 @@ func (r *EC2Resource) getInstanceIds(nameTag string) ([]*string, error) {
return instanceIds, nil
}

// GetUniqueTags returns a slice of unique tags.
// It makes a call through the EC2 client to get all the unique tags in the cluster.
func GetUniqueTags() ([]string, error) {
var uniqueTags []string
// GetNewResources compares the local cluster resources with the ones returned from EC2
// and gives back a list of resources that need to be created on the cluster.
func (m *EC2Monitor) GetNewResources(clusterResources map[string]bool) ([]string, error) {
uniqueTags, err := GetUniqueTags()
if err != nil {
return nil, err
}

slice1, slice2 := setDiff(uniqueTags, clusterResources), setDiff(clusterResources, uniqueTags)
nonMatchingTags := append(slice1, slice2...)

return nonMatchingTags, nil
}

// GetUniqueTags makes a call through the EC2 client to collect all instance tags and returns a set of them
func GetUniqueTags() (map[string]bool, error) {
// Prepare filters
tagKey := "tag:" + resourceMonitorTagKey
tagValue := "true"
Expand Down Expand Up @@ -156,9 +171,16 @@ func GetUniqueTags() ([]string, error) {
}
}

for tag := range tagMap {
uniqueTags = append(uniqueTags, tag)
}
return uniqueTags, nil
return tagMap, nil
}

// setDiff returns the difference between two sets
func setDiff(m1, m2 map[string]bool) []string {
slice := make([]string, 0, len(m1))
for k := range m1 {
if _, ok := m2[k]; !ok {
slice = append(slice, k)
}
}
return slice
}
54 changes: 13 additions & 41 deletions controllers/resourcemonitor_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,6 @@ type ResourceMonitorReconciler struct {
Scheme *runtime.Scheme
}

const (
ResourceType = "ec2"
ResourceNamespace = "default"
)

// sliceToMap returns map, used to convert slice to map
func sliceToMap(slice []string) map[string]bool { //TODO: give a better name
m := make(map[string]bool)
for _, s := range slice {
m[s] = true
}
return m
}

// diff returns a slice of strings, used to compare two maps.
func diff(m1, m2 map[string]bool) []string { //TODO: give a better name
slice := make([]string, 0, len(m1))
for k := range m1 {
if _, ok := m2[k]; !ok {
slice = append(slice, k)
}
}
return slice
}

//+kubebuilder:rbac:groups=manager.kotaico.de,resources=resourcemonitors,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=manager.kotaico.de,resources=resourcemonitors/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=manager.kotaico.de,resources=resourcemonitors/finalizers,verbs=update
Expand All @@ -71,7 +46,7 @@ func diff(m1, m2 map[string]bool) []string { //TODO: give a better name
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
func (r *ResourceMonitorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
var clusterResources []string
clusterResources := make(map[string]bool)

log.Info("Reconcile resource monitor")

Expand All @@ -88,34 +63,31 @@ func (r *ResourceMonitorReconciler) Reconcile(ctx context.Context, req ctrl.Requ
}

for _, rs := range resources.Items {
clusterResources = append(clusterResources, rs.Spec.Tag)
clusterResources[rs.Spec.Tag] = true
}

uniqueTags, err := clients.GetUniqueTags()
monitor, err := clients.MonitorFactory(resourceMonitor.Spec.Type)
if err != nil {
log.Error(err, "Error getting unique tags")
log.Error(err, err.Error())
return ctrl.Result{}, client.IgnoreNotFound(err)
}
uniqueTagsMap := sliceToMap(uniqueTags)
clusterResourcesMap := sliceToMap(clusterResources)

slice1 := diff(uniqueTagsMap, clusterResourcesMap) //TODO: give a better name
slice2 := diff(clusterResourcesMap, uniqueTagsMap) //TODO: give a better name
nonMatchingTags := append(slice1, slice2...)
nonMatchingTags, err := monitor.GetNewResources(clusterResources)
if err != nil {
log.Error(err, err.Error())
return ctrl.Result{}, client.IgnoreNotFound(err)
}

for _, tag := range nonMatchingTags {
resource := &managerv1.Resource{
TypeMeta: metav1.TypeMeta{
APIVersion: "manager.kotaico.de/v1",
Kind: "Resource",
},
ObjectMeta: metav1.ObjectMeta{
Name: tag,
Namespace: ResourceNamespace,
Namespace: resourceMonitor.Namespace,
},
Spec: managerv1.ResourceSpec{
Booked: false,
Tag: tag,
Type: ResourceType,
Type: resourceMonitor.Spec.Type,
},
}
log.Info("creating resources")
Expand All @@ -125,7 +97,7 @@ func (r *ResourceMonitorReconciler) Reconcile(ctx context.Context, req ctrl.Requ
}
}

return ctrl.Result{RequeueAfter: time.Duration(time.Second * 15)}, nil
return ctrl.Result{RequeueAfter: time.Duration(time.Minute * 2)}, nil
}

// SetupWithManager sets up the controller with the Manager.
Expand Down

0 comments on commit 110629c

Please sign in to comment.