Skip to content

Commit f2a235e

Browse files
authored
Install Elastic Agent in Kubernetes cluster in compatible stack version (#569)
* WIP Extract injected metadata * Matching logic * Fix * Fix * Use kibana.Version
1 parent 003885b commit f2a235e

File tree

4 files changed

+114
-8
lines changed

4 files changed

+114
-8
lines changed

internal/install/application_configuration.go

-5
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,6 @@ func (ir ImageRefs) AsEnv() []string {
6464
return vars
6565
}
6666

67-
// DefaultStackImageRefs function selects the appropriate set of Docker image references for the default stack version.
68-
func (ac *ApplicationConfiguration) DefaultStackImageRefs() ImageRefs {
69-
return ac.StackImageRefs(DefaultStackVersion)
70-
}
71-
7267
// StackImageRefs function selects the appropriate set of Docker image references for the given stack version.
7368
func (ac *ApplicationConfiguration) StackImageRefs(version string) ImageRefs {
7469
refs := ac.c.Stack.ImageRefOverridesForVersion(version)

internal/kibana/injected_metadata.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package kibana
6+
7+
import (
8+
"encoding/json"
9+
"fmt"
10+
"html"
11+
"net/http"
12+
"regexp"
13+
14+
"github.com/pkg/errors"
15+
)
16+
17+
var kbnInjectedMetadataRegexp = regexp.MustCompile(`<kbn-injected-metadata data="(.+)"></kbn-injected-metadata>`)
18+
19+
// injectedMetadata represents the Kibana metadata structure exposed in the web UI.
20+
type injectedMetadata struct {
21+
// Stack version
22+
Version string `json:"version"`
23+
}
24+
25+
// Version method returns the Kibana version.
26+
func (c *Client) Version() (string, error) {
27+
statusCode, respBody, err := c.get("/login")
28+
if err != nil {
29+
return "", errors.Wrap(err, "could not reach login endpoint")
30+
}
31+
if statusCode != http.StatusOK {
32+
return "", fmt.Errorf("could not reach login endpoint; API status code = %d; response body = %s", statusCode, string(respBody))
33+
}
34+
35+
im, err := extractInjectedMetadata(respBody)
36+
if err != nil {
37+
return "", errors.Wrap(err, "can't extract injected metadata")
38+
}
39+
return im.Version, nil
40+
}
41+
42+
func extractInjectedMetadata(body []byte) (*injectedMetadata, error) {
43+
rawInjectedMetadata, err := extractRawInjectedMetadata(body)
44+
if err != nil {
45+
return nil, errors.Wrap(err, "can't extract raw metadata")
46+
}
47+
48+
var im injectedMetadata
49+
err = json.Unmarshal(rawInjectedMetadata, &im)
50+
if err != nil {
51+
return nil, errors.Wrap(err, "can't unmarshal raw injected metadata")
52+
}
53+
return &im, nil
54+
}
55+
56+
func extractRawInjectedMetadata(body []byte) ([]byte, error) {
57+
matches := kbnInjectedMetadataRegexp.FindSubmatch(body)
58+
if len(matches) < 2 { // index:0 - matched regexp, index:1 - matched data
59+
return nil, errors.New("expected to find at least one <kbn-injected-metadata> tag")
60+
}
61+
return []byte(html.UnescapeString(string(matches[1]))), nil
62+
}
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package kibana
6+
7+
import (
8+
"testing"
9+
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
// sampleLoginPage represents Kibana login page without redundant styles, fonts, noise in metadata, etc.
14+
var sampleLoginPage = []byte(`<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/><meta name="viewport" content="width=device-width"/><title>Elastic</title><style>
15+
@keyframes kbnProgress {
16+
0% {
17+
transform: scaleX(1) translateX(-100%);
18+
}
19+
20+
100% {
21+
transform: scaleX(1) translateX(100%);
22+
}
23+
}
24+
</style><link rel="stylesheet" type="text/css" href="/44185/bundles/kbn-ui-shared-deps/kbn-ui-shared-deps.css"/><link rel="stylesheet" type="text/css" href="/44185/bundles/kbn-ui-shared-deps/kbn-ui-shared-deps.v8.light.css"/><link rel="stylesheet" type="text/css" href="/node_modules/@kbn/ui-framework/dist/kui_light.css"/><link rel="stylesheet" type="text/css" href="/ui/legacy_light_theme.css"/><meta name="add-styles-here"/><meta name="add-scripts-here"/></head><body><kbn-csp data="{&quot;strictCsp&quot;:false}"></kbn-csp><kbn-injected-metadata data="{&quot;version&quot;:&quot;7.15.1&quot;,&quot;buildNumber&quot;:44185,&quot;branch&quot;:&quot;7.15&quot;,&quot;basePath&quot;:&quot;&quot;}"></kbn-injected-metadata><div class="kbnWelcomeView" id="kbn_loading_message" style="display:none" data-test-subj="kbnLoadingMessage"><div class="kbnLoaderWrap"><h2 class="kbnWelcomeTitle">Please upgrade your browser</h2><div class="kbnWelcomeText">This Elastic installation has strict security requirements enabled that your current browser does not meet.</div></div><script>
25+
// Since this is an unsafe inline script, this code will not run
26+
// in browsers that support content security policy(CSP). This is
27+
// intentional as we check for the existence of __kbnCspNotEnforced__ in
28+
// bootstrap.
29+
window.__kbnCspNotEnforced__ = true;
30+
</script><script src="/bootstrap.js"></script></body></html>`)
31+
32+
func TestExtractRawInjectedMetadata(t *testing.T) {
33+
im, err := extractInjectedMetadata(sampleLoginPage)
34+
require.NoError(t, err)
35+
require.Equal(t, "7.15.1", im.Version)
36+
}

internal/testrunner/runners/system/servicedeployer/kubernetes.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/pkg/errors"
1414

1515
"github.com/elastic/elastic-package/internal/install"
16+
"github.com/elastic/elastic-package/internal/kibana"
1617
"github.com/elastic/elastic-package/internal/kind"
1718
"github.com/elastic/elastic-package/internal/kubectl"
1819
"github.com/elastic/elastic-package/internal/logger"
@@ -141,7 +142,17 @@ func findKubernetesDefinitions(definitionsDir string) ([]string, error) {
141142
func installElasticAgentInCluster() error {
142143
logger.Debug("install Elastic Agent in the Kubernetes cluster")
143144

144-
elasticAgentManagedYaml, err := getElasticAgentYAML()
145+
kibanaClient, err := kibana.NewClient()
146+
if err != nil {
147+
return errors.Wrap(err, "can't create Kibana client")
148+
}
149+
150+
stackVersion, err := kibanaClient.Version()
151+
if err != nil {
152+
return errors.Wrap(err, "can't read Kibana injected metadata")
153+
}
154+
155+
elasticAgentManagedYaml, err := getElasticAgentYAML(stackVersion)
145156
if err != nil {
146157
return errors.Wrap(err, "can't retrieve Kubernetes file for Elastic Agent")
147158
}
@@ -156,7 +167,9 @@ func installElasticAgentInCluster() error {
156167
//go:embed elastic-agent-managed.yaml.tmpl
157168
var elasticAgentManagedYamlTmpl string
158169

159-
func getElasticAgentYAML() ([]byte, error) {
170+
func getElasticAgentYAML(stackVersion string) ([]byte, error) {
171+
logger.Debugf("Prepare YAML definition for Elastic Agent running in stack v%s", stackVersion)
172+
160173
appConfig, err := install.Configuration()
161174
if err != nil {
162175
return nil, errors.Wrap(err, "can't read application configuration")
@@ -167,7 +180,7 @@ func getElasticAgentYAML() ([]byte, error) {
167180
var elasticAgentYaml bytes.Buffer
168181
err = tmpl.Execute(&elasticAgentYaml, map[string]string{
169182
"fleetURL": "http://fleet-server:8220",
170-
"elasticAgentImage": appConfig.DefaultStackImageRefs().ElasticAgent,
183+
"elasticAgentImage": appConfig.StackImageRefs(stackVersion).ElasticAgent,
171184
})
172185
if err != nil {
173186
return nil, errors.Wrap(err, "can't generate elastic agent manifest")

0 commit comments

Comments
 (0)