Skip to content

Commit

Permalink
create separate packages, endpoints, and components for HelmVM cluste…
Browse files Browse the repository at this point in the history
…r management functionalities (#4057)

* create separate packages, endpoints, and components for HelmVM cluster management functionalities
  • Loading branch information
sgalsaleh authored Oct 6, 2023
1 parent 0c5f4fb commit 0eb90a2
Show file tree
Hide file tree
Showing 38 changed files with 1,725 additions and 179 deletions.
39 changes: 26 additions & 13 deletions pkg/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,22 +259,35 @@ func RegisterSessionAuthRoutes(r *mux.Router, kotsStore store.Store, handler KOT
HandlerFunc(middleware.EnforceAccess(policy.BackupRead, handler.GetVeleroStatus))

// KURL
r.Name("Kurl").Path("/api/v1/kurl").HandlerFunc(NotImplemented) // I'm not sure why this is here
r.Name("GenerateNodeJoinCommandWorker").Path("/api/v1/kurl/generate-node-join-command-worker").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.GenerateNodeJoinCommandWorker))
r.Name("GenerateNodeJoinCommandMaster").Path("/api/v1/kurl/generate-node-join-command-master").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.GenerateNodeJoinCommandMaster))
r.Name("GenerateNodeJoinCommandSecondary").Path("/api/v1/kurl/generate-node-join-command-secondary").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.GenerateNodeJoinCommandSecondary))
r.Name("GenerateNodeJoinCommandPrimary").Path("/api/v1/kurl/generate-node-join-command-primary").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.GenerateNodeJoinCommandPrimary))
r.Name("DrainNode").Path("/api/v1/kurl/nodes/{nodeName}/drain").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.DrainNode))
r.Name("DeleteNode").Path("/api/v1/kurl/nodes/{nodeName}").Methods("DELETE").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.DeleteNode))
r.Name("Kurl").Path("/api/v1/kurl").HandlerFunc(NotImplemented)
r.Name("GenerateKurlNodeJoinCommandWorker").Path("/api/v1/kurl/generate-node-join-command-worker").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.GenerateKurlNodeJoinCommandWorker))
r.Name("GenerateKurlNodeJoinCommandMaster").Path("/api/v1/kurl/generate-node-join-command-master").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.GenerateKurlNodeJoinCommandMaster))
r.Name("GenerateKurlNodeJoinCommandSecondary").Path("/api/v1/kurl/generate-node-join-command-secondary").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.GenerateKurlNodeJoinCommandSecondary))
r.Name("GenerateKurlNodeJoinCommandPrimary").Path("/api/v1/kurl/generate-node-join-command-primary").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.GenerateKurlNodeJoinCommandPrimary))
r.Name("DrainKurlNode").Path("/api/v1/kurl/nodes/{nodeName}/drain").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.DrainKurlNode))
r.Name("DeleteKurlNode").Path("/api/v1/kurl/nodes/{nodeName}").Methods("DELETE").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.DeleteKurlNode))
r.Name("GetKurlNodes").Path("/api/v1/kurl/nodes").Methods("GET").
HandlerFunc(middleware.EnforceAccess(policy.ClusterRead, handler.GetKurlNodes))

// HelmVM
r.Name("HelmVM").Path("/api/v1/helmvm").HandlerFunc(NotImplemented)
r.Name("GenerateHelmVMNodeJoinCommandSecondary").Path("/api/v1/helmvm/generate-node-join-command-secondary").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.GenerateHelmVMNodeJoinCommandSecondary))
r.Name("GenerateHelmVMNodeJoinCommandPrimary").Path("/api/v1/helmvm/generate-node-join-command-primary").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.GenerateHelmVMNodeJoinCommandPrimary))
r.Name("DrainHelmVMNode").Path("/api/v1/helmvm/nodes/{nodeName}/drain").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.DrainHelmVMNode))
r.Name("DeleteHelmVMNode").Path("/api/v1/helmvm/nodes/{nodeName}").Methods("DELETE").
HandlerFunc(middleware.EnforceAccess(policy.ClusterWrite, handler.DeleteHelmVMNode))
r.Name("GetHelmVMNodes").Path("/api/v1/helmvm/nodes").Methods("GET").
HandlerFunc(middleware.EnforceAccess(policy.ClusterRead, handler.GetHelmVMNodes))

// Prometheus
r.Name("SetPrometheusAddress").Path("/api/v1/prometheus").Methods("POST").
HandlerFunc(middleware.EnforceAccess(policy.PrometheussettingsWrite, handler.SetPrometheusAddress))
Expand Down
78 changes: 66 additions & 12 deletions pkg/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1136,64 +1136,64 @@ var HandlerPolicyTests = map[string][]HandlerPolicyTest{
},

"Kurl": {}, // Not implemented
"GenerateNodeJoinCommandWorker": {
"GenerateKurlNodeJoinCommandWorker": {
{
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.GenerateNodeJoinCommandWorker(gomock.Any(), gomock.Any())
handlerRecorder.GenerateKurlNodeJoinCommandWorker(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},
"GenerateNodeJoinCommandMaster": {
"GenerateKurlNodeJoinCommandMaster": {
{
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.GenerateNodeJoinCommandMaster(gomock.Any(), gomock.Any())
handlerRecorder.GenerateKurlNodeJoinCommandMaster(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},
"GenerateNodeJoinCommandSecondary": {
"GenerateKurlNodeJoinCommandSecondary": {
{
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.GenerateNodeJoinCommandSecondary(gomock.Any(), gomock.Any())
handlerRecorder.GenerateKurlNodeJoinCommandSecondary(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},
"GenerateNodeJoinCommandPrimary": {
"GenerateKurlNodeJoinCommandPrimary": {
{
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.GenerateNodeJoinCommandPrimary(gomock.Any(), gomock.Any())
handlerRecorder.GenerateKurlNodeJoinCommandPrimary(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},
"DrainNode": {
"DrainKurlNode": {
{
Vars: map[string]string{"nodeName": "node-name"},
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.DrainNode(gomock.Any(), gomock.Any())
handlerRecorder.DrainKurlNode(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},
"DeleteNode": {
"DeleteKurlNode": {
{
Vars: map[string]string{"nodeName": "node-name"},
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.DeleteNode(gomock.Any(), gomock.Any())
handlerRecorder.DeleteKurlNode(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
Expand All @@ -1209,6 +1209,60 @@ var HandlerPolicyTests = map[string][]HandlerPolicyTest{
},
},

"HelmVM": {}, // Not implemented
"GenerateHelmVMNodeJoinCommandSecondary": {
{
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.GenerateHelmVMNodeJoinCommandSecondary(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},
"GenerateHelmVMNodeJoinCommandPrimary": {
{
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.GenerateHelmVMNodeJoinCommandPrimary(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},
"DrainHelmVMNode": {
{
Vars: map[string]string{"nodeName": "node-name"},
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.DrainHelmVMNode(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},
"DeleteHelmVMNode": {
{
Vars: map[string]string{"nodeName": "node-name"},
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.DeleteHelmVMNode(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},
"GetHelmVMNodes": {
{
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.GetHelmVMNodes(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},

// Prometheus
"SetPrometheusAddress": {
{
Expand Down
50 changes: 50 additions & 0 deletions pkg/handlers/helmvm_delete_node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package handlers

import (
"context"
"net/http"

"github.com/gorilla/mux"
"github.com/replicatedhq/kots/pkg/helmvm"
"github.com/replicatedhq/kots/pkg/k8sutil"
"github.com/replicatedhq/kots/pkg/logger"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func (h *Handler) DeleteHelmVMNode(w http.ResponseWriter, r *http.Request) {
client, err := k8sutil.GetClientset()
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}

restconfig, err := k8sutil.GetClusterConfig()
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}

ctx := context.Background()
nodeName := mux.Vars(r)["nodeName"]
node, err := client.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
logger.Errorf("Failed to delete node %s: not found", nodeName)
w.WriteHeader(http.StatusNotFound)
return
}
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}

if err := helmvm.DeleteNode(ctx, client, restconfig, node); err != nil {
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
logger.Infof("Node %s successfully deleted", node.Name)
}
45 changes: 45 additions & 0 deletions pkg/handlers/helmvm_drain_node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package handlers

import (
"context"
"net/http"

"github.com/gorilla/mux"
"github.com/replicatedhq/kots/pkg/helmvm"
"github.com/replicatedhq/kots/pkg/k8sutil"
"github.com/replicatedhq/kots/pkg/logger"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func (h *Handler) DrainHelmVMNode(w http.ResponseWriter, r *http.Request) {
client, err := k8sutil.GetClientset()
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}

ctx := context.Background()
nodeName := mux.Vars(r)["nodeName"]
node, err := client.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
logger.Errorf("Failed to drain node %s: not found", nodeName)
w.WriteHeader(http.StatusNotFound)
return
}
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}

// This pod may get evicted and not be able to respond to the request
go func() {
if err := helmvm.DrainNode(ctx, client, node); err != nil {
logger.Error(err)
return
}
logger.Infof("Node %s successfully drained", node.Name)
}()
}
26 changes: 26 additions & 0 deletions pkg/handlers/helmvm_get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package handlers

import (
"net/http"

"github.com/replicatedhq/kots/pkg/helmvm"
"github.com/replicatedhq/kots/pkg/k8sutil"
"github.com/replicatedhq/kots/pkg/logger"
)

func (h *Handler) GetHelmVMNodes(w http.ResponseWriter, r *http.Request) {
client, err := k8sutil.GetClientset()
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}

nodes, err := helmvm.GetNodes(client)
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
JSON(w, http.StatusOK, nodes)
}
55 changes: 55 additions & 0 deletions pkg/handlers/helmvm_node_join_command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package handlers

import (
"net/http"
"time"

"github.com/replicatedhq/kots/pkg/helmvm"
"github.com/replicatedhq/kots/pkg/k8sutil"
"github.com/replicatedhq/kots/pkg/logger"
)

type GenerateHelmVMNodeJoinCommandResponse struct {
Command []string `json:"command"`
Expiry string `json:"expiry"`
}

func (h *Handler) GenerateHelmVMNodeJoinCommandSecondary(w http.ResponseWriter, r *http.Request) {
client, err := k8sutil.GetClientset()
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}

command, expiry, err := helmvm.GenerateAddNodeCommand(client, false)
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
JSON(w, http.StatusOK, GenerateHelmVMNodeJoinCommandResponse{
Command: command,
Expiry: expiry.Format(time.RFC3339),
})
}

func (h *Handler) GenerateHelmVMNodeJoinCommandPrimary(w http.ResponseWriter, r *http.Request) {
client, err := k8sutil.GetClientset()
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}

command, expiry, err := helmvm.GenerateAddNodeCommand(client, true)
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
JSON(w, http.StatusOK, GenerateHelmVMNodeJoinCommandResponse{
Command: command,
Expiry: expiry.Format(time.RFC3339),
})
}
19 changes: 13 additions & 6 deletions pkg/handlers/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,21 @@ type KOTSHandler interface {
GetVeleroStatus(w http.ResponseWriter, r *http.Request)

// KURL
GenerateNodeJoinCommandWorker(w http.ResponseWriter, r *http.Request)
GenerateNodeJoinCommandMaster(w http.ResponseWriter, r *http.Request)
GenerateNodeJoinCommandSecondary(w http.ResponseWriter, r *http.Request)
GenerateNodeJoinCommandPrimary(w http.ResponseWriter, r *http.Request)
DrainNode(w http.ResponseWriter, r *http.Request)
DeleteNode(w http.ResponseWriter, r *http.Request)
GenerateKurlNodeJoinCommandWorker(w http.ResponseWriter, r *http.Request)
GenerateKurlNodeJoinCommandMaster(w http.ResponseWriter, r *http.Request)
GenerateKurlNodeJoinCommandSecondary(w http.ResponseWriter, r *http.Request)
GenerateKurlNodeJoinCommandPrimary(w http.ResponseWriter, r *http.Request)
DrainKurlNode(w http.ResponseWriter, r *http.Request)
DeleteKurlNode(w http.ResponseWriter, r *http.Request)
GetKurlNodes(w http.ResponseWriter, r *http.Request)

// HelmVM
GenerateHelmVMNodeJoinCommandSecondary(w http.ResponseWriter, r *http.Request)
GenerateHelmVMNodeJoinCommandPrimary(w http.ResponseWriter, r *http.Request)
DrainHelmVMNode(w http.ResponseWriter, r *http.Request)
DeleteHelmVMNode(w http.ResponseWriter, r *http.Request)
GetHelmVMNodes(w http.ResponseWriter, r *http.Request)

// Prometheus
SetPrometheusAddress(w http.ResponseWriter, r *http.Request)

Expand Down
Loading

0 comments on commit 0eb90a2

Please sign in to comment.