| package ionet |
|
|
| import ( |
| "encoding/json" |
| "fmt" |
| "strings" |
| "time" |
|
|
| "github.com/samber/lo" |
| ) |
|
|
| |
| func (c *Client) ListContainers(deploymentID string) (*ContainerList, error) { |
| if deploymentID == "" { |
| return nil, fmt.Errorf("deployment ID cannot be empty") |
| } |
|
|
| endpoint := fmt.Sprintf("/deployment/%s/containers", deploymentID) |
|
|
| resp, err := c.makeRequest("GET", endpoint, nil) |
| if err != nil { |
| return nil, fmt.Errorf("failed to list containers: %w", err) |
| } |
|
|
| var containerList ContainerList |
| if err := decodeDataWithFlexibleTimes(resp.Body, &containerList); err != nil { |
| return nil, fmt.Errorf("failed to parse containers list: %w", err) |
| } |
|
|
| return &containerList, nil |
| } |
|
|
| |
| func (c *Client) GetContainerDetails(deploymentID, containerID string) (*Container, error) { |
| if deploymentID == "" { |
| return nil, fmt.Errorf("deployment ID cannot be empty") |
| } |
| if containerID == "" { |
| return nil, fmt.Errorf("container ID cannot be empty") |
| } |
|
|
| endpoint := fmt.Sprintf("/deployment/%s/container/%s", deploymentID, containerID) |
|
|
| resp, err := c.makeRequest("GET", endpoint, nil) |
| if err != nil { |
| return nil, fmt.Errorf("failed to get container details: %w", err) |
| } |
|
|
| |
| var container Container |
| if err := decodeWithFlexibleTimes(resp.Body, &container); err != nil { |
| return nil, fmt.Errorf("failed to parse container details: %w", err) |
| } |
|
|
| return &container, nil |
| } |
|
|
| |
| func (c *Client) GetContainerJobs(deploymentID, containerID string) (*ContainerList, error) { |
| if deploymentID == "" { |
| return nil, fmt.Errorf("deployment ID cannot be empty") |
| } |
| if containerID == "" { |
| return nil, fmt.Errorf("container ID cannot be empty") |
| } |
|
|
| endpoint := fmt.Sprintf("/deployment/%s/containers-jobs/%s", deploymentID, containerID) |
|
|
| resp, err := c.makeRequest("GET", endpoint, nil) |
| if err != nil { |
| return nil, fmt.Errorf("failed to get container jobs: %w", err) |
| } |
|
|
| var containerList ContainerList |
| if err := decodeDataWithFlexibleTimes(resp.Body, &containerList); err != nil { |
| return nil, fmt.Errorf("failed to parse container jobs: %w", err) |
| } |
|
|
| return &containerList, nil |
| } |
|
|
| |
| func buildLogEndpoint(deploymentID, containerID string, opts *GetLogsOptions) (string, error) { |
| if deploymentID == "" { |
| return "", fmt.Errorf("deployment ID cannot be empty") |
| } |
| if containerID == "" { |
| return "", fmt.Errorf("container ID cannot be empty") |
| } |
|
|
| params := make(map[string]interface{}) |
|
|
| if opts != nil { |
| if opts.Level != "" { |
| params["level"] = opts.Level |
| } |
| if opts.Stream != "" { |
| params["stream"] = opts.Stream |
| } |
| if opts.Limit > 0 { |
| params["limit"] = opts.Limit |
| } |
| if opts.Cursor != "" { |
| params["cursor"] = opts.Cursor |
| } |
| if opts.Follow { |
| params["follow"] = true |
| } |
|
|
| if opts.StartTime != nil { |
| params["start_time"] = opts.StartTime |
| } |
| if opts.EndTime != nil { |
| params["end_time"] = opts.EndTime |
| } |
| } |
|
|
| endpoint := fmt.Sprintf("/deployment/%s/log/%s", deploymentID, containerID) |
| endpoint += buildQueryParams(params) |
|
|
| return endpoint, nil |
| } |
|
|
| |
| func (c *Client) GetContainerLogs(deploymentID, containerID string, opts *GetLogsOptions) (*ContainerLogs, error) { |
| raw, err := c.GetContainerLogsRaw(deploymentID, containerID, opts) |
| if err != nil { |
| return nil, err |
| } |
|
|
| logs := &ContainerLogs{ |
| ContainerID: containerID, |
| } |
|
|
| if raw == "" { |
| return logs, nil |
| } |
|
|
| normalized := strings.ReplaceAll(raw, "\r\n", "\n") |
| lines := strings.Split(normalized, "\n") |
| logs.Logs = lo.FilterMap(lines, func(line string, _ int) (LogEntry, bool) { |
| if strings.TrimSpace(line) == "" { |
| return LogEntry{}, false |
| } |
| return LogEntry{Message: line}, true |
| }) |
|
|
| return logs, nil |
| } |
|
|
| |
| func (c *Client) GetContainerLogsRaw(deploymentID, containerID string, opts *GetLogsOptions) (string, error) { |
| endpoint, err := buildLogEndpoint(deploymentID, containerID, opts) |
| if err != nil { |
| return "", err |
| } |
|
|
| resp, err := c.makeRequest("GET", endpoint, nil) |
| if err != nil { |
| return "", fmt.Errorf("failed to get container logs: %w", err) |
| } |
|
|
| return string(resp.Body), nil |
| } |
|
|
| |
| |
| func (c *Client) StreamContainerLogs(deploymentID, containerID string, opts *GetLogsOptions, callback func(*LogEntry) error) error { |
| if deploymentID == "" { |
| return fmt.Errorf("deployment ID cannot be empty") |
| } |
| if containerID == "" { |
| return fmt.Errorf("container ID cannot be empty") |
| } |
| if callback == nil { |
| return fmt.Errorf("callback function cannot be nil") |
| } |
|
|
| |
| if opts == nil { |
| opts = &GetLogsOptions{} |
| } |
| opts.Follow = true |
|
|
| endpoint, err := buildLogEndpoint(deploymentID, containerID, opts) |
| if err != nil { |
| return err |
| } |
|
|
| |
| |
| for { |
| resp, err := c.makeRequest("GET", endpoint, nil) |
| if err != nil { |
| return fmt.Errorf("failed to stream container logs: %w", err) |
| } |
|
|
| var logs ContainerLogs |
| if err := decodeWithFlexibleTimes(resp.Body, &logs); err != nil { |
| return fmt.Errorf("failed to parse container logs: %w", err) |
| } |
|
|
| |
| for _, logEntry := range logs.Logs { |
| if err := callback(&logEntry); err != nil { |
| return fmt.Errorf("callback error: %w", err) |
| } |
| } |
|
|
| |
| if !logs.HasMore && logs.NextCursor == "" { |
| break |
| } |
|
|
| |
| if logs.NextCursor != "" { |
| opts.Cursor = logs.NextCursor |
| endpoint, err = buildLogEndpoint(deploymentID, containerID, opts) |
| if err != nil { |
| return err |
| } |
| } |
|
|
| |
| time.Sleep(2 * time.Second) |
| } |
|
|
| return nil |
| } |
|
|
| |
| func (c *Client) RestartContainer(deploymentID, containerID string) error { |
| if deploymentID == "" { |
| return fmt.Errorf("deployment ID cannot be empty") |
| } |
| if containerID == "" { |
| return fmt.Errorf("container ID cannot be empty") |
| } |
|
|
| endpoint := fmt.Sprintf("/deployment/%s/container/%s/restart", deploymentID, containerID) |
|
|
| _, err := c.makeRequest("POST", endpoint, nil) |
| if err != nil { |
| return fmt.Errorf("failed to restart container: %w", err) |
| } |
|
|
| return nil |
| } |
|
|
| |
| func (c *Client) StopContainer(deploymentID, containerID string) error { |
| if deploymentID == "" { |
| return fmt.Errorf("deployment ID cannot be empty") |
| } |
| if containerID == "" { |
| return fmt.Errorf("container ID cannot be empty") |
| } |
|
|
| endpoint := fmt.Sprintf("/deployment/%s/container/%s/stop", deploymentID, containerID) |
|
|
| _, err := c.makeRequest("POST", endpoint, nil) |
| if err != nil { |
| return fmt.Errorf("failed to stop container: %w", err) |
| } |
|
|
| return nil |
| } |
|
|
| |
| func (c *Client) ExecuteInContainer(deploymentID, containerID string, command []string) (string, error) { |
| if deploymentID == "" { |
| return "", fmt.Errorf("deployment ID cannot be empty") |
| } |
| if containerID == "" { |
| return "", fmt.Errorf("container ID cannot be empty") |
| } |
| if len(command) == 0 { |
| return "", fmt.Errorf("command cannot be empty") |
| } |
|
|
| reqBody := map[string]interface{}{ |
| "command": command, |
| } |
|
|
| endpoint := fmt.Sprintf("/deployment/%s/container/%s/exec", deploymentID, containerID) |
|
|
| resp, err := c.makeRequest("POST", endpoint, reqBody) |
| if err != nil { |
| return "", fmt.Errorf("failed to execute command in container: %w", err) |
| } |
|
|
| var result map[string]interface{} |
| if err := json.Unmarshal(resp.Body, &result); err != nil { |
| return "", fmt.Errorf("failed to parse execution result: %w", err) |
| } |
|
|
| if output, ok := result["output"].(string); ok { |
| return output, nil |
| } |
|
|
| return string(resp.Body), nil |
| } |
|
|