-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ASCII-2547] Make the GUI serve static files from FS + e2e test (#31187)
- Loading branch information
1 parent
bd7c058
commit dba148c
Showing
8 changed files
with
334 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
174 changes: 174 additions & 0 deletions
174
test/new-e2e/tests/agent-shared-components/gui/gui_common.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2016-present Datadog, Inc. | ||
|
||
package gui | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net" | ||
"net/http" | ||
"net/url" | ||
"path" | ||
"strconv" | ||
"strings" | ||
"testing" | ||
|
||
"net/http/cookiejar" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"golang.org/x/net/html" | ||
|
||
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/components" | ||
) | ||
|
||
const ( | ||
agentAPIPort = 5001 | ||
guiPort = 5002 | ||
guiAPIEndpoint = "/agent/gui/intent" | ||
) | ||
|
||
// assertAgentsUseKey checks that all agents are using the given key. | ||
func getGUIIntentToken(t *assert.CollectT, host *components.RemoteHost, authtoken string) string { | ||
hostHTTPClient := host.NewHTTPClient() | ||
|
||
apiEndpoint := &url.URL{ | ||
Scheme: "https", | ||
Host: net.JoinHostPort("localhost", strconv.Itoa(agentAPIPort)), | ||
Path: guiAPIEndpoint, | ||
} | ||
|
||
req, err := http.NewRequest(http.MethodGet, apiEndpoint.String(), nil) | ||
require.NoErrorf(t, err, "failed to fetch API from %s", apiEndpoint.String()) | ||
|
||
req.Header.Set("Authorization", "Bearer "+authtoken) | ||
|
||
resp, err := hostHTTPClient.Do(req) | ||
require.NoErrorf(t, err, "failed to fetch intent token from %s", apiEndpoint.String()) | ||
defer resp.Body.Close() | ||
|
||
require.Equalf(t, http.StatusOK, resp.StatusCode, "unexpected status code for %s", apiEndpoint.String()) | ||
|
||
url, err := io.ReadAll(resp.Body) | ||
require.NoErrorf(t, err, "failed to read response body from %s", apiEndpoint.String()) | ||
|
||
return string(url) | ||
} | ||
|
||
// assertGuiIsAvailable checks that the Agent GUI server is up and running. | ||
func getGUIClient(t *assert.CollectT, host *components.RemoteHost, authtoken string) *http.Client { | ||
intentToken := getGUIIntentToken(t, host, authtoken) | ||
|
||
guiURL := url.URL{ | ||
Scheme: "http", | ||
Host: net.JoinHostPort("localhost", strconv.Itoa(guiPort)), | ||
Path: "/auth", | ||
RawQuery: url.Values{ | ||
"intent": {intentToken}, | ||
}.Encode(), | ||
} | ||
|
||
jar, err := cookiejar.New(&cookiejar.Options{}) | ||
require.NoError(t, err) | ||
|
||
guiClient := host.NewHTTPClient() | ||
guiClient.Jar = jar | ||
|
||
// Make the GET request | ||
resp, err := guiClient.Get(guiURL.String()) | ||
require.NoErrorf(t, err, "failed to reach GUI at address %s", guiURL.String()) | ||
require.Equalf(t, http.StatusOK, resp.StatusCode, "unexpected status code for %s", guiURL.String()) | ||
defer resp.Body.Close() | ||
|
||
cookies := guiClient.Jar.Cookies(&guiURL) | ||
assert.NotEmpty(t, cookies) | ||
assert.Equal(t, cookies[0].Name, "accessToken", "GUI server didn't the accessToken cookie") | ||
|
||
// Assert redirection to "/" | ||
assert.Equal(t, fmt.Sprintf("http://%v", net.JoinHostPort("localhost", strconv.Itoa(guiPort)))+"/", resp.Request.URL.String(), "GUI auth endpoint didn't redirect to root endpoint") | ||
|
||
return guiClient | ||
} | ||
|
||
func checkStaticFiles(t *testing.T, client *http.Client, host *components.RemoteHost, installPath string) { | ||
|
||
var links []string | ||
var traverse func(*html.Node) | ||
|
||
guiURL := url.URL{ | ||
Scheme: "http", | ||
Host: net.JoinHostPort("localhost", strconv.Itoa(guiPort)), | ||
Path: "/", | ||
} | ||
|
||
// Make the GET request | ||
resp, err := client.Get(guiURL.String()) | ||
require.NoErrorf(t, err, "failed to reach GUI at address %s", guiURL.String()) | ||
require.Equalf(t, http.StatusOK, resp.StatusCode, "unexpected status code for %s", guiURL.String()) | ||
defer resp.Body.Close() | ||
|
||
doc, err := html.Parse(resp.Body) | ||
require.NoErrorf(t, err, "failed to parse HTML response from GUI at address %s", guiURL.String()) | ||
|
||
traverse = func(n *html.Node) { | ||
if n.Type == html.ElementNode { | ||
switch n.Data { | ||
case "link": | ||
for _, attr := range n.Attr { | ||
if attr.Key == "href" { | ||
links = append(links, attr.Val) | ||
} | ||
} | ||
case "script": | ||
for _, attr := range n.Attr { | ||
if attr.Key == "src" { | ||
links = append(links, attr.Val) | ||
} | ||
} | ||
} | ||
} | ||
for c := n.FirstChild; c != nil; c = c.NextSibling { | ||
traverse(c) | ||
} | ||
} | ||
|
||
traverse(doc) | ||
for _, link := range links { | ||
t.Logf("trying to reach asset %v", link) | ||
fullLink := fmt.Sprintf("http://%v/%v", net.JoinHostPort("localhost", strconv.Itoa(guiPort)), link) | ||
resp, err := client.Get(fullLink) | ||
assert.NoErrorf(t, err, "failed to reach GUI asset at address %s", fullLink) | ||
defer resp.Body.Close() | ||
assert.Equalf(t, http.StatusOK, resp.StatusCode, "unexpected status code for %s", fullLink) | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
// We replace windows line break by linux so the tests pass on every OS | ||
bodyContent := strings.Replace(string(body), "\r\n", "\n", -1) | ||
assert.NoErrorf(t, err, "failed to read content of GUI asset at address %s", fullLink) | ||
|
||
// retrieving the served file in the Agent insallation director, removing the "view/" prefix | ||
expectedBody, err := host.ReadFile(path.Join(installPath, "bin", "agent", "dist", "views", strings.TrimLeft(link, "view/"))) | ||
// We replace windows line break by linux so the tests pass on every OS | ||
expectedBodyContent := strings.Replace(string(expectedBody), "\r\n", "\n", -1) | ||
assert.NoErrorf(t, err, "unable to retrieve file %v in the expected served files", link) | ||
|
||
assert.Equalf(t, expectedBodyContent, bodyContent, "content of the file %v is not the same as expected", link) | ||
} | ||
} | ||
|
||
func checkPingEndpoint(t *testing.T, client *http.Client) { | ||
guiURL := url.URL{ | ||
Scheme: "http", | ||
Host: net.JoinHostPort("localhost", strconv.Itoa(guiPort)), | ||
Path: "/agent/ping", | ||
} | ||
|
||
// Make the GET request | ||
resp, err := client.Post(guiURL.String(), "", nil) | ||
require.NoErrorf(t, err, "failed to reach GUI at address %s", guiURL.String()) | ||
require.Equalf(t, http.StatusOK, resp.StatusCode, "unexpected status code for %s", guiURL.String()) | ||
defer resp.Body.Close() | ||
} |
69 changes: 69 additions & 0 deletions
69
test/new-e2e/tests/agent-shared-components/gui/gui_nix_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2016-present Datadog, Inc. | ||
|
||
package gui | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/DataDog/test-infra-definitions/components/datadog/agentparams" | ||
|
||
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e" | ||
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments" | ||
awshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments/aws/host" | ||
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e/client/agentclientparams" | ||
) | ||
|
||
type guiLinuxSuite struct { | ||
e2e.BaseSuite[environments.Host] | ||
} | ||
|
||
func TestGUILinuxSuite(t *testing.T) { | ||
t.Parallel() | ||
e2e.Run(t, &guiLinuxSuite{}, e2e.WithProvisioner(awshost.ProvisionerNoFakeIntake())) | ||
} | ||
|
||
func (v *guiLinuxSuite) TestGUI() { | ||
authTokenFilePath := "/etc/datadog-agent/auth_token" | ||
|
||
config := fmt.Sprintf(`auth_token_file_path: %v | ||
cmd_port: %d | ||
GUI_port: %d`, authTokenFilePath, agentAPIPort, guiPort) | ||
// start the agent with that configuration | ||
v.UpdateEnv(awshost.Provisioner( | ||
awshost.WithAgentOptions( | ||
agentparams.WithAgentConfig(config), | ||
), | ||
awshost.WithAgentClientOptions( | ||
agentclientparams.WithAuthTokenPath(authTokenFilePath), | ||
), | ||
)) | ||
|
||
// get auth token | ||
v.T().Log("Getting the authentication token") | ||
authtokenContent := v.Env().RemoteHost.MustExecute("sudo cat " + authTokenFilePath) | ||
authtoken := strings.TrimSpace(authtokenContent) | ||
|
||
v.T().Log("Testing GUI authentication flow") | ||
|
||
var guiClient *http.Client | ||
// and check that the agents are using the new key | ||
require.EventuallyWithT(v.T(), func(t *assert.CollectT) { | ||
guiClient = getGUIClient(t, v.Env().RemoteHost, authtoken) | ||
}, 30*time.Second, 5*time.Second) | ||
|
||
v.T().Log("Testing GUI static file server") | ||
checkStaticFiles(v.T(), guiClient, v.Env().RemoteHost, "/opt/datadog-agent") | ||
|
||
v.T().Log("Testing GUI ping endpoint") | ||
checkPingEndpoint(v.T(), guiClient) | ||
} |
Oops, something went wrong.