Cloudwatch: Upgrade aws-sdk and display external ids for temporary credentials (#72821)

(under a feature toggle, not yet ready for public testing)
pull/73252/head
Sarah Zinger 2 years ago committed by GitHub
parent c2aeb9882d
commit 09d5483c6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      package.json
  2. 1
      pkg/setting/setting.go
  3. 2
      pkg/tsdb/cloudwatch/resource_handler.go
  4. 29
      pkg/tsdb/cloudwatch/routes/external_id.go
  5. 36
      pkg/tsdb/cloudwatch/routes/external_id_test.go
  6. 4
      public/app/plugins/datasource/cloudwatch/components/ConfigEditor/ConfigEditor.test.tsx
  7. 10
      public/app/plugins/datasource/cloudwatch/components/ConfigEditor/ConfigEditor.tsx
  8. 4
      public/app/plugins/datasource/cloudwatch/resources/ResourcesAPI.ts
  9. 10
      yarn.lock

@ -261,7 +261,7 @@
"@emotion/css": "11.11.2", "@emotion/css": "11.11.2",
"@emotion/react": "11.11.1", "@emotion/react": "11.11.1",
"@glideapps/glide-data-grid": "^5.2.1", "@glideapps/glide-data-grid": "^5.2.1",
"@grafana/aws-sdk": "0.0.47", "@grafana/aws-sdk": "0.1.1",
"@grafana/data": "workspace:*", "@grafana/data": "workspace:*",
"@grafana/e2e-selectors": "workspace:*", "@grafana/e2e-selectors": "workspace:*",
"@grafana/experimental": "1.6.1", "@grafana/experimental": "1.6.1",

@ -1301,6 +1301,7 @@ func (cfg *Cfg) handleAWSConfig() {
} }
cfg.AWSExternalId = awsPluginSec.Key("external_id").Value() cfg.AWSExternalId = awsPluginSec.Key("external_id").Value()
err = os.Setenv(awsds.GrafanaAssumeRoleExternalIdKeyName, cfg.AWSExternalId)
if err != nil { if err != nil {
cfg.Logger.Error(fmt.Sprintf("could not set environment variable '%s'", awsds.GrafanaAssumeRoleExternalIdKeyName), err) cfg.Logger.Error(fmt.Sprintf("could not set environment variable '%s'", awsds.GrafanaAssumeRoleExternalIdKeyName), err)
} }

@ -26,6 +26,8 @@ func (e *cloudWatchExecutor) newResourceMux() *http.ServeMux {
mux.HandleFunc("/accounts", routes.ResourceRequestMiddleware(routes.AccountsHandler, logger, e.getRequestContext)) mux.HandleFunc("/accounts", routes.ResourceRequestMiddleware(routes.AccountsHandler, logger, e.getRequestContext))
mux.HandleFunc("/namespaces", routes.ResourceRequestMiddleware(routes.NamespacesHandler, logger, e.getRequestContext)) mux.HandleFunc("/namespaces", routes.ResourceRequestMiddleware(routes.NamespacesHandler, logger, e.getRequestContext))
mux.HandleFunc("/log-group-fields", routes.ResourceRequestMiddleware(routes.LogGroupFieldsHandler, logger, e.getRequestContext)) mux.HandleFunc("/log-group-fields", routes.ResourceRequestMiddleware(routes.LogGroupFieldsHandler, logger, e.getRequestContext))
mux.HandleFunc("/external-id", routes.ResourceRequestMiddleware(routes.ExternalIdHandler, logger, e.getRequestContext))
return mux return mux
} }

@ -0,0 +1,29 @@
package routes
import (
"context"
"encoding/json"
"net/http"
"net/url"
"os"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
)
type ExternalIdResponse struct {
ExternalId string `json:"externalId"`
}
func ExternalIdHandler(ctx context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, parameters url.Values) ([]byte, *models.HttpError) {
response := ExternalIdResponse{
ExternalId: os.Getenv(awsds.GrafanaAssumeRoleExternalIdKeyName),
}
jsonResponse, err := json.Marshal(response)
if err != nil {
return nil, models.NewHttpError("error in ExternalIdHandler", http.StatusInternalServerError, err)
}
return jsonResponse, nil
}

@ -0,0 +1,36 @@
package routes
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_external_id_route(t *testing.T) {
t.Run("successfully returns an external id from the env", func(t *testing.T) {
t.Setenv("AWS_AUTH_EXTERNAL_ID", "mock-external-id")
rr := httptest.NewRecorder()
handler := http.HandlerFunc(ResourceRequestMiddleware(ExternalIdHandler, logger, nil))
req := httptest.NewRequest("GET", "/external-id", nil)
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
assert.JSONEq(t, `{"externalId":"mock-external-id"}`, rr.Body.String())
})
t.Run("returns an empty string if there is no external id", func(t *testing.T) {
rr := httptest.NewRecorder()
handler := http.HandlerFunc(ResourceRequestMiddleware(ExternalIdHandler, logger, nil))
req := httptest.NewRequest("GET", "/external-id", nil)
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
assert.JSONEq(t, `{"externalId":""}`, rr.Body.String())
})
}

@ -36,6 +36,10 @@ jest.mock('@grafana/runtime', () => ({
get: getMock, get: getMock,
}), }),
getAppEvents: () => mockAppEvents, getAppEvents: () => mockAppEvents,
config: {
...jest.requireActual('@grafana/runtime').config,
awsAssumeRoleEnabled: true,
},
})); }));
const props: Props = { const props: Props = {

@ -57,6 +57,15 @@ export const ConfigEditor = (props: Props) => {
failSubscription.unsubscribe(); failSubscription.unsubscribe();
}; };
}, [options.jsonData.authType, report]); }, [options.jsonData.authType, report]);
const [externalId, setExternalId] = useState('');
useEffect(() => {
if (!externalId && datasource) {
datasource.resources
.getExternalId()
.then(setExternalId)
.catch(() => setExternalId('Unable to fetch externalId'));
}
}, [datasource, externalId]);
return ( return (
<> <>
@ -76,6 +85,7 @@ export const ConfigEditor = (props: Props) => {
); );
}) })
} }
externalId={externalId}
> >
<InlineField label="Namespaces of Custom Metrics" labelWidth={29} tooltip="Namespaces of Custom Metrics."> <InlineField label="Namespaces of Custom Metrics" labelWidth={29} tooltip="Namespaces of Custom Metrics.">
<Input <Input

@ -35,6 +35,10 @@ export class ResourcesAPI extends CloudWatchRequest {
return getBackendSrv().get(`/api/datasources/${this.instanceSettings.id}/resources/${subtype}`, parameters); return getBackendSrv().get(`/api/datasources/${this.instanceSettings.id}/resources/${subtype}`, parameters);
} }
async getExternalId(): Promise<string> {
return await this.memoizedGetRequest<{ externalId: string }>('external-id').then(({ externalId }) => externalId);
}
getAccounts({ region }: ResourceRequest): Promise<Account[]> { getAccounts({ region }: ResourceRequest): Promise<Account[]> {
return this.memoizedGetRequest<Array<ResourceResponse<Account>>>('accounts', { return this.memoizedGetRequest<Array<ResourceResponse<Account>>>('accounts', {
region: this.templateSrv.replace(this.getActualRegion(region)), region: this.templateSrv.replace(this.getActualRegion(region)),

@ -3604,13 +3604,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@grafana/aws-sdk@npm:0.0.47": "@grafana/aws-sdk@npm:0.1.1":
version: 0.0.47 version: 0.1.1
resolution: "@grafana/aws-sdk@npm:0.0.47" resolution: "@grafana/aws-sdk@npm:0.1.1"
dependencies: dependencies:
"@grafana/async-query-data": 0.1.4 "@grafana/async-query-data": 0.1.4
"@grafana/experimental": 1.1.0 "@grafana/experimental": 1.1.0
checksum: 1e9f57bddf08f0c0b432bb8bdd5ad63382c2d40e9d8282469e3add0ea970a80bcd31e852f050412e0d41620d71e53839510261f92333d026bf79a88d7346c626 checksum: 12d1b27b0959a4d16ddf643b360ce067f6debd4141eb28652ff36fece98a99087f865aa2b9f6fda78df2b9e38870a6a7cfa48e0fd1a1ec03de63bc340b344f05
languageName: node languageName: node
linkType: hard linkType: hard
@ -19268,7 +19268,7 @@ __metadata:
"@emotion/eslint-plugin": 11.11.0 "@emotion/eslint-plugin": 11.11.0
"@emotion/react": 11.11.1 "@emotion/react": 11.11.1
"@glideapps/glide-data-grid": ^5.2.1 "@glideapps/glide-data-grid": ^5.2.1
"@grafana/aws-sdk": 0.0.47 "@grafana/aws-sdk": 0.1.1
"@grafana/data": "workspace:*" "@grafana/data": "workspace:*"
"@grafana/e2e": "workspace:*" "@grafana/e2e": "workspace:*"
"@grafana/e2e-selectors": "workspace:*" "@grafana/e2e-selectors": "workspace:*"

Loading…
Cancel
Save