install_agent.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package cluster
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "github.com/porter-dev/porter/api/server/authz"
  7. "github.com/porter-dev/porter/api/server/handlers"
  8. "github.com/porter-dev/porter/api/server/shared"
  9. "github.com/porter-dev/porter/api/server/shared/apierrors"
  10. "github.com/porter-dev/porter/api/server/shared/config"
  11. "github.com/porter-dev/porter/api/types"
  12. "github.com/porter-dev/porter/internal/auth/token"
  13. "github.com/porter-dev/porter/internal/helm"
  14. "github.com/porter-dev/porter/internal/helm/loader"
  15. "github.com/porter-dev/porter/internal/kubernetes"
  16. "github.com/porter-dev/porter/internal/kubernetes/nodes"
  17. "github.com/porter-dev/porter/internal/models"
  18. v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. )
  20. const (
  21. monitoringNodeLabel = "porter.run/workload-kind=monitoring"
  22. olderAgentLabel = "control-plane=controller-manager"
  23. )
  24. type InstallAgentHandler struct {
  25. handlers.PorterHandlerReadWriter
  26. authz.KubernetesAgentGetter
  27. }
  28. func NewInstallAgentHandler(
  29. config *config.Config,
  30. decoderValidator shared.RequestDecoderValidator,
  31. writer shared.ResultWriter,
  32. ) *InstallAgentHandler {
  33. return &InstallAgentHandler{
  34. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  35. KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
  36. }
  37. }
  38. func (c *InstallAgentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  39. proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  40. user, _ := r.Context().Value(types.UserScope).(*models.User)
  41. cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
  42. k8sAgent, err := c.GetAgent(r, cluster, "porter-agent-system")
  43. if err != nil {
  44. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  45. return
  46. }
  47. helmAgent, err := c.GetHelmAgent(r.Context(), r, cluster, "porter-agent-system")
  48. if err != nil {
  49. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  50. return
  51. }
  52. err = checkAndDeleteOlderAgent(k8sAgent, helmAgent)
  53. if err != nil {
  54. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  55. return
  56. }
  57. chart, err := loader.LoadChartPublic(context.Background(), c.Config().ServerConf.DefaultAddonHelmRepoURL, "porter-agent", "")
  58. if err != nil {
  59. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  60. return
  61. }
  62. // create namespace if not exists
  63. _, err = helmAgent.K8sAgent.CreateNamespace("porter-agent-system", nil)
  64. if err != nil {
  65. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  66. return
  67. }
  68. // add api token to values
  69. jwt, err := token.GetTokenForAPI(user.ID, proj.ID)
  70. if err != nil {
  71. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  72. return
  73. }
  74. encoded, err := jwt.EncodeToken(c.Config().TokenConf)
  75. if err != nil {
  76. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  77. return
  78. }
  79. nodes, err := nodes.ListNodesByLabels(k8sAgent.Clientset, "porter.run/workload-kind=monitoring")
  80. hasMonitoringNodes := err == nil && len(nodes) >= 1
  81. porterAgentValues := map[string]interface{}{
  82. "agent": map[string]interface{}{
  83. "porterHost": c.Config().ServerConf.ServerURL,
  84. "porterPort": "443",
  85. "porterToken": encoded,
  86. "clusterID": fmt.Sprintf("%d", cluster.ID),
  87. "projectID": fmt.Sprintf("%d", proj.ID),
  88. },
  89. "loki": map[string]interface{}{},
  90. }
  91. // case on whether a node with porter.run/workload-kind=monitoring exists. If it does, we place loki in that node group.
  92. if hasMonitoringNodes {
  93. sharedNS := map[string]interface{}{
  94. "porter.run/workload-kind": "monitoring",
  95. }
  96. sharedTolerations := []map[string]interface{}{
  97. {
  98. "key": "porter.run/workload-kind",
  99. "operator": "Equal",
  100. "value": "monitoring",
  101. "effect": "NoSchedule",
  102. },
  103. }
  104. porterAgentValues["loki"] = map[string]interface{}{
  105. "nodeSelector": sharedNS,
  106. "tolerations": sharedTolerations,
  107. }
  108. porterAgentValues["nodeSelector"] = sharedNS
  109. porterAgentValues["tolerations"] = sharedTolerations
  110. }
  111. conf := &helm.InstallChartConfig{
  112. Chart: chart,
  113. Name: "porter-agent",
  114. Namespace: "porter-agent-system",
  115. Cluster: cluster,
  116. Repo: c.Repo(),
  117. Values: porterAgentValues,
  118. }
  119. _, err = helmAgent.InstallChart(context.Background(), conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection)
  120. if err != nil {
  121. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
  122. fmt.Errorf("error installing porter-agent: %w", err), http.StatusBadRequest,
  123. ))
  124. return
  125. }
  126. w.WriteHeader(http.StatusOK)
  127. }
  128. func checkAndDeleteOlderAgent(k8sAgent *kubernetes.Agent, helmAgent *helm.Agent) error {
  129. namespaceList, err := k8sAgent.Clientset.CoreV1().Namespaces().List(context.Background(), v1.ListOptions{})
  130. if err != nil {
  131. return fmt.Errorf("error listing namespaces: %w", err)
  132. }
  133. nsExists := false
  134. for _, namespace := range namespaceList.Items {
  135. if namespace.Name == "porter-agent-system" {
  136. nsExists = true
  137. break
  138. }
  139. }
  140. if !nsExists {
  141. return nil
  142. }
  143. // detect if the `porter-agent` release is installed
  144. helmRelease, err := helmAgent.GetRelease(context.Background(), "porter-agent", 0, false)
  145. if err != nil || helmRelease == nil {
  146. return nil
  147. }
  148. _, err = helmAgent.UninstallChart(context.Background(), "porter-agent")
  149. if err != nil {
  150. return err
  151. }
  152. return nil
  153. }