package release import ( "errors" "fmt" "net/http" "strings" "github.com/porter-dev/porter/api/server/authz" "github.com/porter-dev/porter/api/server/handlers" "github.com/porter-dev/porter/api/server/shared" "github.com/porter-dev/porter/api/server/shared/apierrors" "github.com/porter-dev/porter/api/server/shared/config" "github.com/porter-dev/porter/api/types" "github.com/porter-dev/porter/internal/helm/grapher" "github.com/porter-dev/porter/internal/kubernetes" "github.com/porter-dev/porter/internal/models" "github.com/stefanmcshane/helm/pkg/release" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type GetControllersHandler struct { handlers.PorterHandlerReadWriter authz.KubernetesAgentGetter } func NewGetControllersHandler( config *config.Config, writer shared.ResultWriter, ) *GetControllersHandler { return &GetControllersHandler{ PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, nil, writer), KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config), } } func (c *GetControllersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { helmRelease, _ := r.Context().Value(types.ReleaseScope).(*release.Release) cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster) agent, err := c.GetAgent(r, cluster, "") if err != nil { c.HandleAPIError(w, r, apierrors.NewErrInternal(err)) return } yamlArr := grapher.ImportMultiDocYAML([]byte(helmRelease.Manifest)) controllers := grapher.ParseControllers(yamlArr, helmRelease.Namespace) retrievedControllers := []interface{}{} // get current status of each controller for _, controller := range controllers { rc, _, err := getController(controller, agent) if targetErr := kubernetes.IsNotFoundError; errors.Is(err, targetErr) { c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient( fmt.Errorf("%s/%s of kind %s was not found", controller.Namespace, controller.Name, controller.Kind), http.StatusNotFound, )) return } else if err != nil { c.HandleAPIError(w, r, apierrors.NewErrInternal(err)) return } retrievedControllers = append(retrievedControllers, rc) } c.WriteResult(w, r, retrievedControllers) } func getController(controller grapher.Object, agent *kubernetes.Agent) (rc interface{}, selector *metav1.LabelSelector, err error) { switch strings.ToLower(controller.Kind) { case "deployment": obj, err := agent.GetDeployment(controller) if err != nil { err = fmt.Errorf("error getting deployment: %w", err) return nil, nil, err } return obj, obj.Spec.Selector, nil case "statefulset": obj, err := agent.GetStatefulSet(controller) if err != nil { err = fmt.Errorf("error getting stateful set: %w", err) return nil, nil, err } return obj, obj.Spec.Selector, nil case "daemonset": obj, err := agent.GetDaemonSet(controller) if err != nil { err = fmt.Errorf("error getting daemon set: %w", err) return nil, nil, err } return obj, obj.Spec.Selector, nil case "replicaset": obj, err := agent.GetReplicaSet(controller) if err != nil { err = fmt.Errorf("error getting replica set: %w", err) return nil, nil, err } return obj, obj.Spec.Selector, nil case "cronjob": obj, err := agent.GetCronJob(controller) if err != nil { err = fmt.Errorf("error getting cron job %w", err) return nil, nil, err } res := &metav1.LabelSelector{ MatchLabels: make(map[string]string), } for key, val := range obj.Spec.JobTemplate.Labels { res.MatchLabels[key] = val } return obj, res, nil case "job": obj, err := agent.GetJob(controller) if err != nil { err = fmt.Errorf("error getting job: %w", err) return nil, nil, err } return obj, obj.Spec.Selector, nil } return nil, nil, fmt.Errorf("not a valid controller") }