package agent import ( "context" "crypto/tls" "errors" "fmt" "io" "net/http" "strconv" "time" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/url" "github.com/portainer/portainer/pkg/libhttp/ssrf" "github.com/rs/zerolog/log" ) // GetAgentVersionAndPlatform returns the agent version and platform // // it sends a ping to the agent and parses the version and platform from the headers func GetAgentVersionAndPlatform(endpointUrl string, tlsConfig *tls.Config) (portainer.AgentPlatform, string, error) { //nolint:forbidigo if err := ssrf.CheckURL(context.Background(), endpointUrl); err != nil { return 0, "", err } httpCli := &http.Client{Timeout: 3 * time.Second} if tlsConfig != nil { httpCli.Transport = ssrf.NewTransport(tlsConfig) } parsedURL, err := url.ParseURL(endpointUrl + "/ping") if err != nil { return 0, "", err } parsedURL.Scheme = "https" req, err := http.NewRequest(http.MethodGet, parsedURL.String(), nil) if err != nil { return 0, "", err } resp, err := httpCli.Do(req) if err != nil { return 0, "", err } _, _ = io.Copy(io.Discard, resp.Body) if err := resp.Body.Close(); err != nil { log.Warn().Err(err).Msg("failed to close response body") } if resp.StatusCode != http.StatusNoContent { return 0, "", fmt.Errorf("Failed request with status %d", resp.StatusCode) } version := resp.Header.Get(portainer.PortainerAgentHeader) if version == "" { return 0, "", errors.New("Version Header is missing") } agentPlatformHeader := resp.Header.Get(portainer.HTTPResponseAgentPlatform) if agentPlatformHeader == "" { return 0, "", errors.New("Agent Platform Header is missing") } agentPlatformNumber, err := strconv.Atoi(agentPlatformHeader) if err != nil { return 0, "", err } if agentPlatformNumber == 0 { return 0, "", errors.New("Agent platform is invalid") } return portainer.AgentPlatform(agentPlatformNumber), version, nil }