Use new GithubAuth flow in GitOps plugin
This commit is contained in:
committed by
Fredrik Adelöw
parent
c32a61afe8
commit
a306bf2c27
@@ -79,6 +79,14 @@ export interface ListClusterRequest {
|
||||
gitHubToken: string;
|
||||
}
|
||||
|
||||
export interface GithubUserInfoRequest {
|
||||
accessToken: string;
|
||||
}
|
||||
|
||||
export interface GithubUserInfoResponse {
|
||||
login: string;
|
||||
}
|
||||
|
||||
export class FetchError extends Error {
|
||||
get name(): string {
|
||||
return this.constructor.name;
|
||||
@@ -100,6 +108,7 @@ export type GitOpsApi = {
|
||||
cloneClusterFromTemplate(req: CloneFromTemplateRequest): Promise<any>;
|
||||
applyProfiles(req: ApplyProfileRequest): Promise<any>;
|
||||
listClusters(req: ListClusterRequest): Promise<ListClusterStatusesResponse>;
|
||||
fetchUserInfo(req: GithubUserInfoRequest): Promise<GithubUserInfoResponse>;
|
||||
};
|
||||
|
||||
export const gitOpsApiRef = createApiRef<GitOpsApi>({
|
||||
@@ -116,6 +125,19 @@ export class GitOpsRestApi implements GitOpsApi {
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
async fetchUserInfo(
|
||||
req: GithubUserInfoRequest,
|
||||
): Promise<GithubUserInfoResponse> {
|
||||
const resp = await fetch(`https://api.github.com/user`, {
|
||||
method: 'get',
|
||||
headers: new Headers({
|
||||
Authorization: `token ${req.accessToken}`,
|
||||
}),
|
||||
});
|
||||
if (!resp.ok) throw await FetchError.forResponse(resp);
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
async fetchLog(req: PollLogRequest): Promise<StatusResponse> {
|
||||
return await this.fetch<StatusResponse>(`/api/cluster/run-status`, {
|
||||
method: 'post',
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import React, { FC, useState } from 'react';
|
||||
import {
|
||||
Content,
|
||||
ContentHeader,
|
||||
@@ -25,32 +25,28 @@ import {
|
||||
Progress,
|
||||
HeaderLabel,
|
||||
useApi,
|
||||
githubAuthApiRef,
|
||||
} from '@backstage/core';
|
||||
|
||||
import ClusterTable from '../ClusterTable/ClusterTable';
|
||||
import { Button } from '@material-ui/core';
|
||||
import { useAsync, useLocalStorage } from 'react-use';
|
||||
import { useAsync } from 'react-use';
|
||||
import { gitOpsApiRef, ListClusterStatusesResponse } from '../../api';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
|
||||
const ClusterList: FC<{}> = () => {
|
||||
const [loginInfo] = useLocalStorage<{
|
||||
token: string;
|
||||
username: string;
|
||||
name: string;
|
||||
}>('githubLoginDetails', {
|
||||
token: '',
|
||||
username: '',
|
||||
name: 'Guest',
|
||||
});
|
||||
|
||||
const api = useApi(gitOpsApiRef);
|
||||
const githubAuth = useApi(githubAuthApiRef);
|
||||
const [githubUsername, setGithubUsername] = useState(String);
|
||||
|
||||
const { loading, error, value } = useAsync<ListClusterStatusesResponse>(
|
||||
() => {
|
||||
async () => {
|
||||
const accessToken = await githubAuth.getAccessToken();
|
||||
const userInfo = await api.fetchUserInfo({ accessToken });
|
||||
setGithubUsername(userInfo.login);
|
||||
return api.listClusters({
|
||||
gitHubToken: loginInfo.token,
|
||||
gitHubUser: loginInfo.username,
|
||||
gitHubToken: accessToken,
|
||||
gitHubUser: githubUsername,
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -73,9 +69,6 @@ const ClusterList: FC<{}> = () => {
|
||||
Please make sure that you start GitOps-API backend on localhost port
|
||||
3008 before using this plugin.
|
||||
</Alert>
|
||||
<Alert severity="info">
|
||||
If you're Guest, please login via GitHub first.
|
||||
</Alert>
|
||||
</div>
|
||||
</Content>
|
||||
);
|
||||
@@ -100,7 +93,7 @@ const ClusterList: FC<{}> = () => {
|
||||
return (
|
||||
<Page theme={pageTheme.home}>
|
||||
<Header title="GitOps-managed Clusters">
|
||||
<HeaderLabel label="Welcome" value={loginInfo.name} />
|
||||
<HeaderLabel label="Welcome" value={githubUsername} />
|
||||
</Header>
|
||||
{content}
|
||||
</Page>
|
||||
|
||||
@@ -24,21 +24,16 @@ import {
|
||||
Progress,
|
||||
HeaderLabel,
|
||||
useApi,
|
||||
githubAuthApiRef,
|
||||
} from '@backstage/core';
|
||||
|
||||
import { Link } from '@material-ui/core';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useLocalStorage } from 'react-use';
|
||||
import { gitOpsApiRef, Status } from '../../api';
|
||||
import { transformRunStatus } from '../ProfileCatalog';
|
||||
|
||||
const ClusterPage: FC<{}> = () => {
|
||||
const params = useParams() as { owner: string; repo: string };
|
||||
const [loginInfo] = useLocalStorage<{
|
||||
token: string;
|
||||
username: string;
|
||||
name: string;
|
||||
}>('githubLoginDetails');
|
||||
|
||||
const [pollingLog, setPollingLog] = useState(true);
|
||||
const [runStatus, setRunStatus] = useState<Status[]>([]);
|
||||
@@ -46,6 +41,9 @@ const ClusterPage: FC<{}> = () => {
|
||||
const [showProgress, setShowProgress] = useState(true);
|
||||
|
||||
const api = useApi(gitOpsApiRef);
|
||||
const githubAuth = useApi(githubAuthApiRef);
|
||||
const [githubAccessToken, setGithubAccessToken] = useState(String);
|
||||
const [githubUsername, setGithubUsername] = useState(String);
|
||||
|
||||
const columns = [
|
||||
{ field: 'status', title: 'Status' },
|
||||
@@ -53,11 +51,22 @@ const ClusterPage: FC<{}> = () => {
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const fetchGithubUserInfo = async () => {
|
||||
const accessToken = await githubAuth.getAccessToken();
|
||||
const userInfo = await api.fetchUserInfo({ accessToken });
|
||||
setGithubAccessToken(accessToken);
|
||||
setGithubUsername(userInfo.login);
|
||||
};
|
||||
|
||||
if (!githubAccessToken || !githubUsername) {
|
||||
fetchGithubUserInfo();
|
||||
}
|
||||
|
||||
if (pollingLog) {
|
||||
const interval = setInterval(async () => {
|
||||
const resp = await api.fetchLog({
|
||||
gitHubToken: loginInfo.token,
|
||||
gitHubUser: loginInfo.username,
|
||||
gitHubToken: githubAccessToken,
|
||||
gitHubUser: githubUsername,
|
||||
targetOrg: params.owner,
|
||||
targetRepo: params.repo,
|
||||
});
|
||||
@@ -72,12 +81,12 @@ const ClusterPage: FC<{}> = () => {
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
return () => {};
|
||||
}, [pollingLog, api, loginInfo, params]);
|
||||
}, [pollingLog, api, params, githubAuth, githubAccessToken, githubUsername]);
|
||||
|
||||
return (
|
||||
<Page theme={pageTheme.home}>
|
||||
<Header title={`Cluster ${params.owner}/${params.repo}`}>
|
||||
<HeaderLabel label="Welcome" value={loginInfo.name} />
|
||||
<HeaderLabel label="Welcome" value={githubUsername} />
|
||||
</Header>
|
||||
<Content>
|
||||
<Progress hidden={!showProgress} />
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
StatusPending,
|
||||
StatusAborted,
|
||||
useApi,
|
||||
githubAuthApiRef,
|
||||
} from '@backstage/core';
|
||||
import { TextField, List, ListItem, Link } from '@material-ui/core';
|
||||
|
||||
@@ -111,17 +112,12 @@ const ProfileCatalog: FC<{}> = () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const [loginInfo] = useLocalStorage('githubLoginDetails', {
|
||||
name: 'Guest',
|
||||
username: '',
|
||||
token: '',
|
||||
});
|
||||
const [templateRepo] = useLocalStorage<string>('gitops-template-repo');
|
||||
const [gitopsProfiles] = useLocalStorage<string[]>('gitops-profiles');
|
||||
|
||||
const [showProgress, setShowProgress] = useState(false);
|
||||
const [pollingLog, setPollingLog] = useState(false);
|
||||
const [gitHubOrg, setGitHubOrg] = useState(loginInfo.username);
|
||||
const [gitHubOrg, setGitHubOrg] = useState(String);
|
||||
const [gitHubRepo, setGitHubRepo] = useState('new-cluster');
|
||||
const [awsAccessKeyId, setAwsAccessKeyId] = useState(String);
|
||||
const [awsSecretAccessKey, setAwsSecretAccessKey] = useState(String);
|
||||
@@ -129,13 +125,28 @@ const ProfileCatalog: FC<{}> = () => {
|
||||
const [runLink, setRunLink] = useState<string>('');
|
||||
|
||||
const api = useApi(gitOpsApiRef);
|
||||
const githubAuth = useApi(githubAuthApiRef);
|
||||
const [githubAccessToken, setGithubAccessToken] = useState(String);
|
||||
const [githubUsername, setGithubUsername] = useState(String);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchGithubUserInfo = async () => {
|
||||
const accessToken = await githubAuth.getAccessToken();
|
||||
const userInfo = await api.fetchUserInfo({ accessToken });
|
||||
setGithubAccessToken(accessToken);
|
||||
setGithubUsername(userInfo.login);
|
||||
setGitHubOrg(userInfo.login);
|
||||
};
|
||||
|
||||
if (!githubAccessToken || !githubUsername) {
|
||||
fetchGithubUserInfo();
|
||||
}
|
||||
|
||||
if (pollingLog) {
|
||||
const interval = setInterval(async () => {
|
||||
const resp = await api.fetchLog({
|
||||
gitHubToken: loginInfo.token,
|
||||
gitHubUser: loginInfo.username,
|
||||
gitHubToken: githubAccessToken,
|
||||
gitHubUser: githubUsername,
|
||||
targetOrg: gitHubOrg,
|
||||
targetRepo: gitHubRepo,
|
||||
});
|
||||
@@ -150,7 +161,15 @@ const ProfileCatalog: FC<{}> = () => {
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
return () => {};
|
||||
}, [pollingLog, api, gitHubOrg, gitHubRepo, loginInfo]);
|
||||
}, [
|
||||
pollingLog,
|
||||
api,
|
||||
gitHubOrg,
|
||||
gitHubRepo,
|
||||
githubAuth,
|
||||
githubAccessToken,
|
||||
githubUsername,
|
||||
]);
|
||||
|
||||
const showFailureMessage = (msg: string) => {
|
||||
setRunStatus(
|
||||
@@ -182,8 +201,8 @@ const ProfileCatalog: FC<{}> = () => {
|
||||
|
||||
const cloneResponse = await api.cloneClusterFromTemplate({
|
||||
templateRepository: templateRepo,
|
||||
gitHubToken: loginInfo.token,
|
||||
gitHubUser: loginInfo.username,
|
||||
gitHubToken: githubAccessToken,
|
||||
gitHubUser: githubUsername,
|
||||
targetOrg: gitHubOrg,
|
||||
targetRepo: gitHubRepo,
|
||||
secrets: {
|
||||
@@ -200,8 +219,8 @@ const ProfileCatalog: FC<{}> = () => {
|
||||
}
|
||||
|
||||
const applyProfileResp = await api.applyProfiles({
|
||||
gitHubToken: loginInfo.token,
|
||||
gitHubUser: loginInfo.username,
|
||||
gitHubToken: githubAccessToken,
|
||||
gitHubUser: githubUsername,
|
||||
targetOrg: gitHubOrg,
|
||||
targetRepo: gitHubRepo,
|
||||
profiles: gitopsProfiles,
|
||||
@@ -215,8 +234,8 @@ const ProfileCatalog: FC<{}> = () => {
|
||||
}
|
||||
|
||||
const clusterStateResp = await api.changeClusterState({
|
||||
gitHubToken: loginInfo.token,
|
||||
gitHubUser: loginInfo.username,
|
||||
gitHubToken: githubAccessToken,
|
||||
gitHubUser: githubUsername,
|
||||
targetOrg: gitHubOrg,
|
||||
targetRepo: gitHubRepo,
|
||||
clusterState: 'present',
|
||||
@@ -244,7 +263,7 @@ const ProfileCatalog: FC<{}> = () => {
|
||||
title="Create GitOps-managed Cluster"
|
||||
subtitle="Kubernetes cluster with ready-to-use profiles"
|
||||
>
|
||||
<HeaderLabel label="Welcome" value={loginInfo.name} />
|
||||
<HeaderLabel label="Welcome" value={githubUsername} />
|
||||
</Header>
|
||||
<Content>
|
||||
<ContentHeader title="Create Cluster">
|
||||
|
||||
@@ -18,12 +18,17 @@ import { createPlugin } from '@backstage/core';
|
||||
import ProfileCatalog from './components/ProfileCatalog';
|
||||
import ClusterPage from './components/ClusterPage';
|
||||
import ClusterList from './components/ClusterList';
|
||||
import {
|
||||
gitOpsClusterListRoute,
|
||||
gitOpsClusterDetailsRoute,
|
||||
gitOpsClusterCreateRoute,
|
||||
} from './routes';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'gitops-profiles',
|
||||
register({ router }) {
|
||||
router.registerRoute('/gitops-clusters', ClusterList);
|
||||
router.registerRoute('/gitops-cluster/:owner/:repo', ClusterPage);
|
||||
router.registerRoute('/gitops-cluster-create', ProfileCatalog);
|
||||
router.addRoute(gitOpsClusterListRoute, ClusterList);
|
||||
router.addRoute(gitOpsClusterDetailsRoute, ClusterPage);
|
||||
router.addRoute(gitOpsClusterCreateRoute, ProfileCatalog);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createRouteRef } from '@backstage/core';
|
||||
|
||||
const NoIcon = () => null;
|
||||
|
||||
export const gitOpsClusterListRoute = createRouteRef({
|
||||
icon: NoIcon,
|
||||
path: '/gitops-clusters',
|
||||
title: 'GitOps Clusters',
|
||||
});
|
||||
|
||||
export const gitOpsClusterDetailsRoute = createRouteRef({
|
||||
icon: NoIcon,
|
||||
path: '/gitops-cluster/:owner/:repo',
|
||||
title: 'GitOps Cluster details',
|
||||
});
|
||||
|
||||
export const gitOpsClusterCreateRoute = createRouteRef({
|
||||
icon: NoIcon,
|
||||
path: '/gitops-cluster-create',
|
||||
title: 'GitOps Cluster create',
|
||||
});
|
||||
Reference in New Issue
Block a user