v1.3.2
This commit is contained in:
commit
8c768f2a70
3 changed files with 109 additions and 40 deletions
|
|
@ -7,22 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## v1.3.3
|
||||
|
||||
## v1.3.4 (2023-06-29)
|
||||
|
||||
This version contains just some small cleanup and CI changes 🙂
|
||||
|
||||
## v1.3.3 (2023-06-28)
|
||||
## v1.3.2 (2023-06-29)
|
||||
|
||||
### Added
|
||||
|
||||
- Add metric "how many alert groups user was notified of" to Prometheus exporter ([#2334](https://github.com/grafana/oncall/pull/2334/))
|
||||
|
||||
## v1.3.2 (2023-06-28)
|
||||
|
||||
### Changed
|
||||
|
||||
- Change permissions used during setup to better represent actions being taken by @mderynck ([#2242](https://github.com/grafana/oncall/pull/2242))
|
||||
- Display 100000+ in stats when there are more than 100000 alert groups in the result ([#1901](https://github.com/grafana/oncall/pull/1901))
|
||||
- Change OnCall plugin to use service accounts and api tokens for communicating with backend, by @mderynck ([#2385](https://github.com/grafana/oncall/pull/2385))
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
|||
|
|
@ -136,22 +136,75 @@ class PluginState {
|
|||
this.grafanaBackend.post(this.GRAFANA_PLUGIN_SETTINGS_URL, { ...data, enabled, pinned: true });
|
||||
|
||||
static readonly KEYS_BASE_URL = '/api/auth/keys';
|
||||
static readonly ONCALL_KEY_NAME = 'OnCall';
|
||||
static readonly SERVICE_ACCOUNTS_BASE_URL = '/api/serviceaccounts';
|
||||
static readonly ONCALL_SERVICE_ACCOUNT_NAME = 'sa-autogen-OnCall';
|
||||
static readonly SERVICE_ACCOUNTS_SEARCH_URL = `${PluginState.SERVICE_ACCOUNTS_BASE_URL}/search?query=${PluginState.ONCALL_SERVICE_ACCOUNT_NAME}`;
|
||||
|
||||
static getGrafanaToken = async () => {
|
||||
const keys = await this.grafanaBackend.get(this.KEYS_BASE_URL);
|
||||
return keys.find((key: { id: number; name: string; role: string }) => key.name === 'OnCall');
|
||||
static getServiceAccount = async () => {
|
||||
const serviceAccounts = await this.grafanaBackend.get(this.SERVICE_ACCOUNTS_SEARCH_URL);
|
||||
return serviceAccounts.serviceAccounts.length > 0 ? serviceAccounts.serviceAccounts[0] : null;
|
||||
};
|
||||
|
||||
static getOrCreateServiceAccount = async () => {
|
||||
const serviceAccount = await this.getServiceAccount();
|
||||
if (serviceAccount) {
|
||||
return serviceAccount;
|
||||
}
|
||||
|
||||
return await this.grafanaBackend.post(this.SERVICE_ACCOUNTS_BASE_URL, {
|
||||
name: this.ONCALL_SERVICE_ACCOUNT_NAME,
|
||||
role: 'Admin',
|
||||
isDisabled: false,
|
||||
});
|
||||
};
|
||||
|
||||
static getTokenFromServiceAccount = async (serviceAccount) => {
|
||||
const tokens = await this.grafanaBackend.get(`${this.SERVICE_ACCOUNTS_BASE_URL}/${serviceAccount.id}/tokens`);
|
||||
return tokens.find((key: { id: number; name: string; role: string }) => key.name === PluginState.ONCALL_KEY_NAME);
|
||||
};
|
||||
|
||||
/**
|
||||
* This will satisfy a check for an existing key regardless of if the key is an older api key or under a
|
||||
* service account.
|
||||
*/
|
||||
static getGrafanaToken = async () => {
|
||||
const serviceAccount = await this.getServiceAccount();
|
||||
if (serviceAccount) {
|
||||
return await this.getTokenFromServiceAccount(serviceAccount);
|
||||
}
|
||||
|
||||
const keys = await this.grafanaBackend.get(this.KEYS_BASE_URL);
|
||||
const oncallApiKeys = keys.find(
|
||||
(key: { id: number; name: string; role: string }) => key.name === PluginState.ONCALL_KEY_NAME
|
||||
);
|
||||
if (oncallApiKeys) {
|
||||
return oncallApiKeys;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create service account and api token belonging to it instead of using api keys
|
||||
*/
|
||||
static createGrafanaToken = async () => {
|
||||
const serviceAccount = await this.getOrCreateServiceAccount();
|
||||
const existingToken = await this.getTokenFromServiceAccount(serviceAccount);
|
||||
if (existingToken) {
|
||||
await this.grafanaBackend.delete(
|
||||
`${this.SERVICE_ACCOUNTS_BASE_URL}/${serviceAccount.id}/tokens/${existingToken.id}`
|
||||
);
|
||||
}
|
||||
|
||||
const existingKey = await this.getGrafanaToken();
|
||||
if (existingKey) {
|
||||
await this.grafanaBackend.delete(`${this.KEYS_BASE_URL}/${existingKey.id}`);
|
||||
}
|
||||
|
||||
return await this.grafanaBackend.post(this.KEYS_BASE_URL, {
|
||||
name: 'OnCall',
|
||||
return await this.grafanaBackend.post(`${this.SERVICE_ACCOUNTS_BASE_URL}/${serviceAccount.id}/tokens`, {
|
||||
name: PluginState.ONCALL_KEY_NAME,
|
||||
role: 'Admin',
|
||||
secondsToLive: null,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -179,38 +179,59 @@ describe('PluginState.updateGrafanaPluginSettings', () => {
|
|||
});
|
||||
|
||||
describe('PluginState.createGrafanaToken', () => {
|
||||
test.each([true, false])('it calls the proper methods - existing key: %s', async (onCallKeyExists) => {
|
||||
const baseUrl = '/api/auth/keys';
|
||||
const onCallKeyId = 12345;
|
||||
const onCallKeyName = 'OnCall';
|
||||
const onCallKey = { name: onCallKeyName, id: onCallKeyId };
|
||||
const existingKeys = [{ name: 'foo', id: 9595 }];
|
||||
const cases = [
|
||||
[true, true, false],
|
||||
[true, false, false],
|
||||
[false, true, true],
|
||||
[false, true, false],
|
||||
[false, false, false],
|
||||
];
|
||||
|
||||
PluginState.grafanaBackend.get = jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce(onCallKeyExists ? [...existingKeys, onCallKey] : existingKeys);
|
||||
PluginState.grafanaBackend.delete = jest.fn();
|
||||
PluginState.grafanaBackend.post = jest.fn();
|
||||
test.each(cases)(
|
||||
'it calls the proper methods - existing key: %s, existing sa: %s, existing token: %s',
|
||||
async (apiKeyExists, saExists, apiTokenExists) => {
|
||||
const baseUrl = PluginState.KEYS_BASE_URL;
|
||||
const serviceAccountBaseUrl = PluginState.SERVICE_ACCOUNTS_BASE_URL;
|
||||
const apiKeyId = 12345;
|
||||
const apiKeyName = PluginState.ONCALL_KEY_NAME;
|
||||
const apiKey = { name: apiKeyName, id: apiKeyId };
|
||||
const saId = 33333;
|
||||
const serviceAccount = { id: saId };
|
||||
|
||||
await PluginState.createGrafanaToken();
|
||||
PluginState.getGrafanaToken = jest.fn().mockReturnValueOnce(apiKeyExists ? apiKey : null);
|
||||
PluginState.grafanaBackend.delete = jest.fn();
|
||||
PluginState.grafanaBackend.post = jest.fn();
|
||||
|
||||
expect(PluginState.grafanaBackend.get).toHaveBeenCalledTimes(1);
|
||||
expect(PluginState.grafanaBackend.get).toHaveBeenCalledWith(baseUrl);
|
||||
PluginState.getServiceAccount = jest.fn().mockReturnValueOnce(saExists ? serviceAccount : null);
|
||||
PluginState.getOrCreateServiceAccount = jest.fn().mockReturnValueOnce(serviceAccount);
|
||||
PluginState.getTokenFromServiceAccount = jest.fn().mockReturnValueOnce(apiTokenExists ? apiKey : null);
|
||||
|
||||
if (onCallKeyExists) {
|
||||
expect(PluginState.grafanaBackend.delete).toHaveBeenCalledTimes(1);
|
||||
expect(PluginState.grafanaBackend.delete).toHaveBeenCalledWith(`${baseUrl}/${onCallKeyId}`);
|
||||
} else {
|
||||
expect(PluginState.grafanaBackend.delete).not.toHaveBeenCalled();
|
||||
await PluginState.createGrafanaToken();
|
||||
|
||||
expect(PluginState.getGrafanaToken).toHaveBeenCalledTimes(1);
|
||||
|
||||
if (apiKeyExists) {
|
||||
expect(PluginState.grafanaBackend.delete).toHaveBeenCalledTimes(1);
|
||||
expect(PluginState.grafanaBackend.delete).toHaveBeenCalledWith(`${baseUrl}/${apiKey.id}`);
|
||||
} else if (apiTokenExists) {
|
||||
expect(PluginState.grafanaBackend.delete).toHaveBeenCalledTimes(1);
|
||||
expect(PluginState.grafanaBackend.delete).toHaveBeenCalledWith(
|
||||
`${serviceAccountBaseUrl}/${serviceAccount.id}/tokens/${apiKey.id}`
|
||||
);
|
||||
} else {
|
||||
expect(PluginState.grafanaBackend.delete).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
expect(PluginState.grafanaBackend.post).toHaveBeenCalledTimes(1);
|
||||
expect(PluginState.grafanaBackend.post).toHaveBeenCalledWith(
|
||||
`${serviceAccountBaseUrl}/${serviceAccount.id}/tokens`,
|
||||
{
|
||||
name: apiKeyName,
|
||||
role: 'Admin',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
expect(PluginState.grafanaBackend.post).toHaveBeenCalledTimes(1);
|
||||
expect(PluginState.grafanaBackend.post).toHaveBeenCalledWith(baseUrl, {
|
||||
name: onCallKeyName,
|
||||
role: 'Admin',
|
||||
secondsToLive: null,
|
||||
});
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
describe('PluginState.getPluginSyncStatus', () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue