Skip to content

Commit e27598a

Browse files
authored
Add options for API key in clients (#2317)
Add also a new environment variable for shellinit. These changes are enough to run some commands, but more changes are required to run system tests.
1 parent 3ece4ee commit e27598a

File tree

12 files changed

+126
-40
lines changed

12 files changed

+126
-40
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ The output of this command is intended to be evaluated by the current shell. For
480480

481481
Relevant environment variables are:
482482

483+
- ELASTIC_PACKAGE_ELASTICSEARCH_API_KEY
483484
- ELASTIC_PACKAGE_ELASTICSEARCH_HOST
484485
- ELASTIC_PACKAGE_ELASTICSEARCH_USERNAME
485486
- ELASTIC_PACKAGE_ELASTICSEARCH_PASSWORD
@@ -690,13 +691,15 @@ There are available some environment variables that could be used to change some
690691

691692
- To configure the Elastic stack to be used by `elastic-package`:
692693
- `ELASTIC_PACKAGE_ELASTICSEARCH_HOST`: Host of the elasticsearch (e.g. https://127.0.0.1:9200)
693-
- `ELASTIC_PACKAGE_ELASTICSEARCH_USERNAME`: User name to connect to elasticsearch (e.g. elastic)
694+
- `ELASTIC_PACKAGE_ELASTICSEARCH_API_KEY`: API key to connect to elasticsearch and kibana. When set it takes precedence over username and password.
695+
- `ELASTIC_PACKAGE_ELASTICSEARCH_USERNAME`: User name to connect to elasticsearch and kibana (e.g. elastic)
694696
- `ELASTIC_PACKAGE_ELASTICSEARCH_PASSWORD`: Password of that user.
695697
- `ELASTIC_PACKAGE_ELASTICSEARCH_KIBANA_HOST`: Kibana URL (e.g. https://127.0.0.1:5601)
696698
- `ELASTIC_PACKAGE_ELASTICSEARCH_CA_CERT`: Path to the CA certificate to connect to the Elastic stack services.
697699

698700
- To configure an external metricstore while running benchmarks (more info at [system benchmarking docs](https://github.com/elastic/elastic-package/blob/main/docs/howto/system_benchmarking.md#setting-up-an-external-metricstore) or [rally benchmarking docs](https://github.com/elastic/elastic-package/blob/main/docs/howto/rally_benchmarking.md#setting-up-an-external-metricstore)):
699701
- `ELASTIC_PACKAGE_ESMETRICSTORE_HOST`: Host of the elasticsearch (e.g. https://127.0.0.1:9200)
702+
- `ELASTIC_PACKAGE_ESMETRICSTORE_API_KEY`: API key to connect to elasticsearch and kibana. When set it takes precedence over username and password.
700703
- `ELASTIC_PACKAGE_ESMETRICSTORE_USERNAME`: Username to connect to elasticsearch (e.g. elastic)
701704
- `ELASTIC_PACKAGE_ESMETRICSTORE_PASSWORD`: Password for the user.
702705
- `ELASTIC_PACKAGE_ESMETRICSTORE_CA_CERT`: Path to the CA certificate to connect to the Elastic stack services.

cmd/benchmark.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,16 +655,18 @@ func systemCommandAction(cmd *cobra.Command, args []string) error {
655655

656656
func initializeESMetricsClient(ctx context.Context) (*elasticsearch.Client, error) {
657657
address := os.Getenv(benchcommon.ESMetricstoreHostEnv)
658+
apiKey := os.Getenv(benchcommon.ESMetricstoreAPIKeyEnv)
658659
user := os.Getenv(benchcommon.ESMetricstoreUsernameEnv)
659660
pass := os.Getenv(benchcommon.ESMetricstorePasswordEnv)
660661
cacert := os.Getenv(benchcommon.ESMetricstoreCACertificateEnv)
661-
if address == "" || user == "" || pass == "" {
662+
if address == "" || ((user == "" || pass == "") && apiKey == "") {
662663
logger.Debugf("can't initialize metricstore, missing environment configuration")
663664
return nil, nil
664665
}
665666

666667
esClient, err := stack.NewElasticsearchClient(
667668
elasticsearch.OptionWithAddress(address),
669+
elasticsearch.OptionWithAPIKey(apiKey),
668670
elasticsearch.OptionWithUsername(user),
669671
elasticsearch.OptionWithPassword(pass),
670672
elasticsearch.OptionWithCertificateAuthority(cacert),

cmd/stack.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ The output of this command is intended to be evaluated by the current shell. For
5555
5656
Relevant environment variables are:
5757
58+
- ELASTIC_PACKAGE_ELASTICSEARCH_API_KEY
5859
- ELASTIC_PACKAGE_ELASTICSEARCH_HOST
5960
- ELASTIC_PACKAGE_ELASTICSEARCH_USERNAME
6061
- ELASTIC_PACKAGE_ELASTICSEARCH_PASSWORD

internal/benchrunner/runners/common/env.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package common
77
import "github.com/elastic/elastic-package/internal/environment"
88

99
var (
10+
ESMetricstoreAPIKeyEnv = environment.WithElasticPackagePrefix("ESMETRICSTORE_API_KEY")
1011
ESMetricstoreHostEnv = environment.WithElasticPackagePrefix("ESMETRICSTORE_HOST")
1112
ESMetricstoreUsernameEnv = environment.WithElasticPackagePrefix("ESMETRICSTORE_USERNAME")
1213
ESMetricstorePasswordEnv = environment.WithElasticPackagePrefix("ESMETRICSTORE_PASSWORD")

internal/elasticsearch/client.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type ClusterStateRequest = esapi.ClusterStateRequest
3535
// clientOptions are used to configure a client.
3636
type clientOptions struct {
3737
address string
38+
apiKey string
3839
username string
3940
password string
4041

@@ -47,6 +48,13 @@ type clientOptions struct {
4748

4849
type ClientOption func(*clientOptions)
4950

51+
// OptionWithAPIKey sets the API key to be used by the client for authentication.
52+
func OptionWithAPIKey(apiKey string) ClientOption {
53+
return func(opts *clientOptions) {
54+
opts.apiKey = apiKey
55+
}
56+
}
57+
5058
// OptionWithAddress sets the address to be used by the client.
5159
func OptionWithAddress(address string) ClientOption {
5260
return func(opts *clientOptions) {
@@ -109,6 +117,7 @@ func NewConfig(customOptions ...ClientOption) (elasticsearch.Config, error) {
109117

110118
config := elasticsearch.Config{
111119
Addresses: []string{options.address},
120+
APIKey: options.apiKey,
112121
Username: options.username,
113122
Password: options.password,
114123
}

internal/kibana/client.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ var ErrUndefinedHost = errors.New("missing kibana host")
2727
// Client is responsible for exporting dashboards from Kibana.
2828
type Client struct {
2929
host string
30+
apiKey string
3031
username string
3132
password string
3233

@@ -94,6 +95,13 @@ func Address(address string) ClientOption {
9495
}
9596
}
9697

98+
// APIKey option sets the API key to be used by the client for authentication.
99+
func APIKey(apiKey string) ClientOption {
100+
return func(c *Client) {
101+
c.apiKey = apiKey
102+
}
103+
}
104+
97105
// TLSSkipVerify option disables TLS verification.
98106
func TLSSkipVerify() ClientOption {
99107
return func(c *Client) {
@@ -182,7 +190,11 @@ func (c *Client) newRequest(ctx context.Context, method, resourcePath string, re
182190
return nil, fmt.Errorf("could not create %v request to Kibana API resource: %s: %w", method, resourcePath, err)
183191
}
184192

185-
req.SetBasicAuth(c.username, c.password)
193+
if c.apiKey != "" {
194+
req.Header.Set("Authorization", "ApiKey "+c.apiKey)
195+
} else {
196+
req.SetBasicAuth(c.username, c.password)
197+
}
186198
req.Header.Add("content-type", "application/json")
187199
req.Header.Add("kbn-xsrf", install.DefaultStackVersion)
188200

internal/stack/clients.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
func NewElasticsearchClient(customOptions ...elasticsearch.ClientOption) (*elasticsearch.Client, error) {
2222
options := []elasticsearch.ClientOption{
2323
elasticsearch.OptionWithAddress(os.Getenv(ElasticsearchHostEnv)),
24+
elasticsearch.OptionWithAPIKey(os.Getenv(ElasticsearchAPIKeyEnv)),
2425
elasticsearch.OptionWithPassword(os.Getenv(ElasticsearchPasswordEnv)),
2526
elasticsearch.OptionWithUsername(os.Getenv(ElasticsearchUsernameEnv)),
2627
elasticsearch.OptionWithCertificateAuthority(os.Getenv(CACertificateEnv)),
@@ -58,6 +59,10 @@ func NewElasticsearchClientFromProfile(profile *profile.Profile, customOptions .
5859
elasticsearchHost = profileConfig.ElasticsearchHostPort
5960
logger.Debugf("Connecting with Elasticsearch host from current profile (profile: %s, host: %q)", profile.ProfileName, elasticsearchHost)
6061
}
62+
elasticsearchAPIKey, found := os.LookupEnv(ElasticsearchAPIKeyEnv)
63+
if !found {
64+
elasticsearchAPIKey = profileConfig.ElasticsearchAPIKey
65+
}
6166
elasticsearchPassword, found := os.LookupEnv(ElasticsearchPasswordEnv)
6267
if !found {
6368
elasticsearchPassword = profileConfig.ElasticsearchPassword
@@ -73,6 +78,7 @@ func NewElasticsearchClientFromProfile(profile *profile.Profile, customOptions .
7378

7479
options := []elasticsearch.ClientOption{
7580
elasticsearch.OptionWithAddress(elasticsearchHost),
81+
elasticsearch.OptionWithAPIKey(elasticsearchAPIKey),
7682
elasticsearch.OptionWithPassword(elasticsearchPassword),
7783
elasticsearch.OptionWithUsername(elasticsearchUsername),
7884
elasticsearch.OptionWithCertificateAuthority(caCertificate),
@@ -86,6 +92,7 @@ func NewElasticsearchClientFromProfile(profile *profile.Profile, customOptions .
8692
func NewKibanaClient(customOptions ...kibana.ClientOption) (*kibana.Client, error) {
8793
options := []kibana.ClientOption{
8894
kibana.Address(os.Getenv(KibanaHostEnv)),
95+
kibana.APIKey(os.Getenv(ElasticsearchAPIKeyEnv)),
8996
kibana.Password(os.Getenv(ElasticsearchPasswordEnv)),
9097
kibana.Username(os.Getenv(ElasticsearchUsernameEnv)),
9198
kibana.CertificateAuthority(os.Getenv(CACertificateEnv)),
@@ -123,6 +130,10 @@ func NewKibanaClientFromProfile(profile *profile.Profile, customOptions ...kiban
123130
kibanaHost = profileConfig.KibanaHostPort
124131
logger.Debugf("Connecting with Kibana host from current profile (profile: %s, host: %q)", profile.ProfileName, kibanaHost)
125132
}
133+
elasticsearchAPIKey, found := os.LookupEnv(ElasticsearchAPIKeyEnv)
134+
if !found {
135+
elasticsearchAPIKey = profileConfig.ElasticsearchAPIKey
136+
}
126137
elasticsearchPassword, found := os.LookupEnv(ElasticsearchPasswordEnv)
127138
if !found {
128139
elasticsearchPassword = profileConfig.ElasticsearchPassword
@@ -138,6 +149,7 @@ func NewKibanaClientFromProfile(profile *profile.Profile, customOptions ...kiban
138149

139150
options := []kibana.ClientOption{
140151
kibana.Address(kibanaHost),
152+
kibana.APIKey(elasticsearchAPIKey),
141153
kibana.Password(elasticsearchPassword),
142154
kibana.Username(elasticsearchUsername),
143155
kibana.CertificateAuthority(caCertificate),
@@ -158,5 +170,10 @@ func FindCACertificate(profile *profile.Profile) (string, error) {
158170
caCertPath = profileConfig.CACertificatePath
159171
}
160172

173+
// Avoid returning an empty certificate path, fallback to the default path.
174+
if caCertPath == "" {
175+
caCertPath = profile.Path(CACertificateFile)
176+
}
177+
161178
return caCertPath, nil
162179
}

internal/stack/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Config struct {
2020
Provider string `json:"provider,omitempty"`
2121
Parameters map[string]string `json:"parameters,omitempty"`
2222

23+
ElasticsearchAPIKey string `json:"elasticsearch_api_key,omitempty"`
2324
ElasticsearchHost string `json:"elasticsearch_host,omitempty"`
2425
ElasticsearchUsername string `json:"elasticsearch_username,omitempty"`
2526
ElasticsearchPassword string `json:"elasticsearch_password,omitempty"`

internal/stack/initconfig.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
)
1010

1111
type InitConfig struct {
12+
ElasticsearchAPIKey string
1213
ElasticsearchHostPort string
1314
ElasticsearchUsername string
1415
ElasticsearchPassword string
@@ -23,6 +24,7 @@ func StackInitConfig(profile *profile.Profile) (*InitConfig, error) {
2324
}
2425

2526
return &InitConfig{
27+
ElasticsearchAPIKey: config.ElasticsearchAPIKey,
2628
ElasticsearchHostPort: config.ElasticsearchHost,
2729
ElasticsearchUsername: config.ElasticsearchUsername,
2830
ElasticsearchPassword: config.ElasticsearchPassword,

internal/stack/shellinit.go

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ import (
2020

2121
// Environment variables describing the stack.
2222
var (
23+
ElasticsearchAPIKeyEnv = environment.WithElasticPackagePrefix("ELASTICSEARCH_API_KEY")
2324
ElasticsearchHostEnv = environment.WithElasticPackagePrefix("ELASTICSEARCH_HOST")
24-
ElasticsearchUsernameEnv = environment.WithElasticPackagePrefix("ELASTICSEARCH_USERNAME")
2525
ElasticsearchPasswordEnv = environment.WithElasticPackagePrefix("ELASTICSEARCH_PASSWORD")
26+
ElasticsearchUsernameEnv = environment.WithElasticPackagePrefix("ELASTICSEARCH_USERNAME")
2627
KibanaHostEnv = environment.WithElasticPackagePrefix("KIBANA_HOST")
2728
CACertificateEnv = environment.WithElasticPackagePrefix("CA_CERT")
2829
)
@@ -38,60 +39,70 @@ func ShellInit(elasticStackProfile *profile.Profile, shellType string) (string,
3839
if err != nil {
3940
return "", nil
4041
}
42+
return shellInitWithConfig(config, shellType)
43+
}
4144

42-
// NOTE: to add new env vars, the template need to be adjusted
43-
t, err := initTemplate(shellType)
45+
func shellInitWithConfig(config *InitConfig, shellType string) (string, error) {
46+
pattern, err := selectPattern(shellType)
4447
if err != nil {
4548
return "", fmt.Errorf("cannot get shell init template: %w", err)
4649
}
4750

48-
return fmt.Sprintf(t,
49-
ElasticsearchHostEnv, config.ElasticsearchHostPort,
50-
ElasticsearchUsernameEnv, config.ElasticsearchUsername,
51-
ElasticsearchPasswordEnv, config.ElasticsearchPassword,
52-
KibanaHostEnv, config.KibanaHostPort,
53-
CACertificateEnv, config.CACertificatePath,
54-
), nil
51+
template := genTemplate(pattern)
52+
return template([]generatorEnvVar{
53+
{ElasticsearchAPIKeyEnv, config.ElasticsearchAPIKey},
54+
{ElasticsearchHostEnv, config.ElasticsearchHostPort},
55+
{ElasticsearchUsernameEnv, config.ElasticsearchUsername},
56+
{ElasticsearchPasswordEnv, config.ElasticsearchPassword},
57+
{KibanaHostEnv, config.KibanaHostPort},
58+
{CACertificateEnv, config.CACertificatePath},
59+
}), nil
60+
}
61+
62+
type generatorEnvVar struct {
63+
name string
64+
value string
5565
}
5666

5767
const (
5868
// shell init code for POSIX compliant shells.
5969
// IEEE POSIX Shell and Tools portion of the IEEE POSIX specification (IEEE Standard 1003.1)
60-
posixTemplate = `export %s=%s
61-
export %s=%s
62-
export %s=%s
63-
export %s=%s
64-
export %s=%s`
70+
posixPattern = `export %s=%s`
6571

6672
// fish shell init code.
6773
// fish shell is similar but not compliant to POSIX.
68-
fishTemplate = `set -x %s %s;
69-
set -x %s %s;
70-
set -x %s %s;
71-
set -x %s %s;
72-
set -x %s %s;`
74+
fishPattern = `set -x %s %s;`
7375

7476
// PowerShell init code.
7577
// Output to be evaluated with `elastic-package stack shellinit | Invoke-Expression
76-
powershellTemplate = `$Env:%s="%s";
77-
$Env:%s="%s";
78-
$Env:%s="%s";
79-
$Env:%s="%s";
80-
$Env:%s="%s";`
78+
powershellPattern = `$Env:%s="%s";`
8179
)
8280

81+
func genTemplate(pattern string) func([]generatorEnvVar) string {
82+
return func(vars []generatorEnvVar) string {
83+
var builder strings.Builder
84+
for i, v := range vars {
85+
fmt.Fprintf(&builder, pattern, v.name, v.value)
86+
if i < len(vars)-1 {
87+
builder.WriteString("\n")
88+
}
89+
}
90+
return builder.String()
91+
}
92+
}
93+
8394
// availableShellTypes list all available values for s in initTemplate
8495
var availableShellTypes = []string{"bash", "dash", "fish", "sh", "zsh", "pwsh", "powershell"}
8596

86-
// InitTemplate returns code templates for shell initialization
87-
func initTemplate(s string) (string, error) {
97+
// SelectPattern returns the patterns to generate list of environment variables for each shell.
98+
func selectPattern(s string) (string, error) {
8899
switch s {
89100
case "bash", "dash", "sh", "zsh":
90-
return posixTemplate, nil
101+
return posixPattern, nil
91102
case "fish":
92-
return fishTemplate, nil
103+
return fishPattern, nil
93104
case "pwsh", "powershell":
94-
return powershellTemplate, nil
105+
return powershellPattern, nil
95106
default:
96107
return "", errors.New("shell type is unknown, should be one of " + strings.Join(availableShellTypes, ", "))
97108
}

0 commit comments

Comments
 (0)