Files
Maestro/src/handlers/actions/actions.go
T

93 lines
3.3 KiB
Go

// Copyright (C) 2026 Dcntrlized
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package actions
import (
"context"
"fmt"
"io"
"net/http"
"git.dcntrlized.org/Dcntrlized/Maestro/lib/docker"
"git.dcntrlized.org/Dcntrlized/Maestro/lib/logger"
)
func RunAction(w http.ResponseWriter, r *http.Request) {
// Runs an action on the container specified by the actionName and containerId path parameters.
actionName := r.PathValue("actionName")
containerId := r.PathValue("containerId")
logger.Debug("Received request to run action %s on container id %s", actionName, containerId)
// Get IP address of the container using containerId
dockerClient, err := docker.NewDockerClient()
if err != nil {
http.Error(w, "Failed to create Docker client", http.StatusInternalServerError)
return
}
inspectData, err := dockerClient.ContainerInspect(context.Background(), containerId)
if err != nil {
http.Error(w, "Failed to inspect container", http.StatusInternalServerError)
return
}
network, _, err := docker.ExtractNetworkInfo(context.Background(), dockerClient, inspectData)
if err != nil {
http.Error(w, "Failed to extract network info", http.StatusInternalServerError)
return
}
// Construct the URL for the action endpoint
actionURL := fmt.Sprintf("http://%s:%d/action/%s", network.IPAddress.Address, network.Ports[0].Host, actionName) // Assuming network.Ports[0] is the main port. Might change in the future
// Forward the request to the container's action endpoint
resp, err := http.Get(actionURL)
if err != nil {
logger.Debug("Failed to forward action request %s to container %s: %w", actionName, containerId, err)
http.Error(w, "Failed to forward request to container", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
logger.Debug("Failed to read container error response body: %v", err)
}
responseBodyString := string(bodyBytes)
if resp.StatusCode == 404 {
logger.Info("The requested action does not exist")
http.Error(w, "The requested action does not exist", http.StatusNotFound)
}
logger.Info("Container responded with non-2xx code")
logger.Debug("Container: %s | Called URL: %s | Status: %d | Response: %s",
containerId, actionURL, resp.StatusCode, responseBodyString)
http.Error(w, "Container responded with non-2xx code", http.StatusInternalServerError)
return
}
// Copy the response from the container back to the original response writer
w.WriteHeader(resp.StatusCode)
_, err = io.Copy(w, resp.Body)
if err != nil {
http.Error(w, "Failed to read response from container", http.StatusInternalServerError)
return
}
}