Skip to content

Commit

Permalink
Update calls to the kubeops service to require cluster. (#1838)
Browse files Browse the repository at this point in the history
* Update calls to the kubeops service to require cluster.

* Update tiller-proxy uri's with cluster aware routes (not-used)
  • Loading branch information
absoludity committed Jul 2, 2020
1 parent c7d8291 commit 2bfcb3a
Show file tree
Hide file tree
Showing 18 changed files with 181 additions and 95 deletions.
7 changes: 7 additions & 0 deletions cmd/tiller-proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,20 @@ func main() {
}

// Routes
// Deprecate non-cluster-aware URIs.
apiv1 := r.PathPrefix("/v1").Subrouter()
apiv1.Methods("GET").Path("/releases").Handler(handlerutil.WithoutParams(h.ListAllReleases))
apiv1.Methods("GET").Path("/namespaces/{namespace}/releases").Handler(handlerutil.WithParams(h.ListReleases))
apiv1.Methods("POST").Path("/namespaces/{namespace}/releases").Handler(handlerutil.WithParams(h.CreateRelease))
apiv1.Methods("GET").Path("/namespaces/{namespace}/releases/{releaseName}").Handler(handlerutil.WithParams(h.GetRelease))
apiv1.Methods("PUT").Path("/namespaces/{namespace}/releases/{releaseName}").Handler(handlerutil.WithParams(h.OperateRelease))
apiv1.Methods("DELETE").Path("/namespaces/{namespace}/releases/{releaseName}").Handler(handlerutil.WithParams(h.DeleteRelease))
apiv1.Methods("GET").Path("/clusters/{cluster}/releases").Handler(handlerutil.WithoutParams(h.ListAllReleases))
apiv1.Methods("GET").Path("/clusters/{cluster}/namespaces/{namespace}/releases").Handler(handlerutil.WithParams(h.ListReleases))
apiv1.Methods("POST").Path("/clusters/{cluster}/namespaces/{namespace}/releases").Handler(handlerutil.WithParams(h.CreateRelease))
apiv1.Methods("GET").Path("/clusters/{cluster}/namespaces/{namespace}/releases/{releaseName}").Handler(handlerutil.WithParams(h.GetRelease))
apiv1.Methods("PUT").Path("/clusters/{cluster}/namespaces/{namespace}/releases/{releaseName}").Handler(handlerutil.WithParams(h.OperateRelease))
apiv1.Methods("DELETE").Path("/clusters/{cluster}/namespaces/{namespace}/releases/{releaseName}").Handler(handlerutil.WithParams(h.DeleteRelease))

// Backend routes unrelated to tiller-proxy functionality.
err = backendHandlers.SetupDefaultRoutes(r.PathPrefix("/backend/v1").Subrouter())
Expand Down
45 changes: 26 additions & 19 deletions dashboard/src/actions/apps.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,20 @@ describe("fetches applications", () => {
{ type: getType(actions.apps.listApps), payload: true },
{ type: getType(actions.apps.receiveAppList), payload: [] },
];
await store.dispatch(actions.apps.fetchAppsWithUpdateInfo("default", true));
await store.dispatch(actions.apps.fetchAppsWithUpdateInfo("default-cluster", "default", true));
expect(store.getActions()).toEqual(expectedActions);
expect(listAppsMock.mock.calls[0]).toEqual(["default", true]);
expect(listAppsMock.mock.calls[0]).toEqual(["default-cluster", "default", true]);
});
it("fetches default applications", async () => {
const expectedActions = [
{ type: getType(actions.apps.listApps), payload: false },
{ type: getType(actions.apps.receiveAppList), payload: [] },
];
await store.dispatch(actions.apps.fetchAppsWithUpdateInfo("default", false));
await store.dispatch(
actions.apps.fetchAppsWithUpdateInfo("default-cluster", "default-ns", false),
);
expect(store.getActions()).toEqual(expectedActions);
expect(listAppsMock.mock.calls[0]).toEqual(["default", false]);
expect(listAppsMock.mock.calls[0]).toEqual(["default-cluster", "default-ns", false]);
});

describe("fetches chart updates", () => {
Expand Down Expand Up @@ -91,7 +93,7 @@ describe("fetches applications", () => {
},
},
];
await store.dispatch(actions.apps.fetchAppsWithUpdateInfo("default", false));
await store.dispatch(actions.apps.fetchAppsWithUpdateInfo("default-c", "default-ns", false));
expect(store.getActions()).toEqual(expectedActions);
});

Expand Down Expand Up @@ -129,7 +131,7 @@ describe("fetches applications", () => {
},
},
];
await store.dispatch(actions.apps.fetchAppsWithUpdateInfo("default", false));
await store.dispatch(actions.apps.fetchAppsWithUpdateInfo("default-c", "default-ns", false));
expect(store.getActions()).toEqual(expectedActions);
});

Expand Down Expand Up @@ -166,7 +168,7 @@ describe("fetches applications", () => {
},
},
];
await store.dispatch(actions.apps.fetchAppsWithUpdateInfo("default", false));
await store.dispatch(actions.apps.fetchAppsWithUpdateInfo("default-c", "default-ns", false));
expect(store.getActions()).toEqual(expectedActions);
});
});
Expand All @@ -183,22 +185,22 @@ describe("delete applications", () => {
App.delete = deleteAppOrig;
});
it("delete an application", async () => {
await store.dispatch(actions.apps.deleteApp("default", "foo", false));
await store.dispatch(actions.apps.deleteApp("default-c", "default-ns", "foo", false));
const expectedActions = [
{ type: getType(actions.apps.requestDeleteApp) },
{ type: getType(actions.apps.receiveDeleteApp) },
];
expect(store.getActions()).toEqual(expectedActions);
expect(deleteAppMock.mock.calls[0]).toEqual(["default", "foo", false]);
expect(deleteAppMock.mock.calls[0]).toEqual(["default-c", "default-ns", "foo", false]);
});
it("delete and purge an application", async () => {
await store.dispatch(actions.apps.deleteApp("default", "foo", true));
await store.dispatch(actions.apps.deleteApp("default-c", "default-ns", "foo", true));
const expectedActions = [
{ type: getType(actions.apps.requestDeleteApp) },
{ type: getType(actions.apps.receiveDeleteApp) },
];
expect(store.getActions()).toEqual(expectedActions);
expect(deleteAppMock.mock.calls[0]).toEqual(["default", "foo", true]);
expect(deleteAppMock.mock.calls[0]).toEqual(["default-c", "default-ns", "foo", true]);
});
it("delete and throw an error", async () => {
const error = new Error("something went wrong!");
Expand All @@ -209,7 +211,9 @@ describe("delete applications", () => {
deleteAppMock.mockImplementation(() => {
throw error;
});
expect(await store.dispatch(actions.apps.deleteApp("default", "foo", true))).toBe(false);
expect(
await store.dispatch(actions.apps.deleteApp("default-c", "default-ns", "foo", true)),
).toBe(false);
expect(store.getActions()).toEqual(expectedActions);
});
});
Expand Down Expand Up @@ -297,8 +301,9 @@ describe("deploy chart", () => {

describe("upgradeApp", () => {
const provisionCMD = actions.apps.upgradeApp(
"my-version" as any,
"default-c",
"kubeapps-ns",
"my-version" as any,
definedNamespaces.all,
"my-release",
);
Expand All @@ -314,9 +319,10 @@ describe("upgradeApp", () => {
];
expect(store.getActions()).toEqual(expectedActions);
expect(App.upgrade).toHaveBeenCalledWith(
definedNamespaces.all,
"my-release",
"default-c",
"kubeapps-ns",
"my-release",
definedNamespaces.all,
"my-version" as any,
undefined,
);
Expand All @@ -342,8 +348,9 @@ describe("upgradeApp", () => {
it("returns false and dispatches UnprocessableEntity if the given values don't satisfy the schema ", async () => {
const res = await store.dispatch(
actions.apps.upgradeApp(
"my-version" as any,
"default-c",
"kubeapps-ns",
"my-version" as any,
"default",
"my-release",
"foo: 1",
Expand All @@ -368,7 +375,7 @@ describe("upgradeApp", () => {
});

describe("rollbackApp", () => {
const provisionCMD = actions.apps.rollbackApp("default", "my-release", 1);
const provisionCMD = actions.apps.rollbackApp("default-c", "default-ns", "my-release", 1);

it("success and re-request apps info", async () => {
App.rollback = jest.fn().mockImplementationOnce(() => true);
Expand All @@ -383,8 +390,8 @@ describe("rollbackApp", () => {
{ type: getType(actions.apps.selectApp), payload: true },
];
expect(store.getActions()).toEqual(expectedActions);
expect(App.rollback).toHaveBeenCalledWith("default", "my-release", 1);
expect(App.getRelease).toHaveBeenCalledWith("default", "my-release");
expect(App.rollback).toHaveBeenCalledWith("default-c", "default-ns", "my-release", 1);
expect(App.getRelease).toHaveBeenCalledWith("default-c", "default-ns", "my-release");
});

it("dispatches an error", async () => {
Expand Down
25 changes: 16 additions & 9 deletions dashboard/src/actions/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,14 @@ const allActions = [
export type AppsAction = ActionType<typeof allActions[number]>;

export function getApp(
cluster: string,
namespace: string,
releaseName: string,
): ThunkAction<Promise<hapi.release.Release | undefined>, IStoreState, null, AppsAction> {
return async dispatch => {
dispatch(requestApps());
try {
const app = await App.getRelease(namespace, releaseName);
const app = await App.getRelease(cluster, namespace, releaseName);
dispatch(selectApp(app));
return app;
} catch (e) {
Expand Down Expand Up @@ -157,12 +158,13 @@ function getAppUpdateInfo(
}

export function getAppWithUpdateInfo(
cluster: string,
namespace: string,
releaseName: string,
): ThunkAction<Promise<void>, IStoreState, null, AppsAction> {
return async dispatch => {
try {
const app = await dispatch(getApp(namespace, releaseName));
const app = await dispatch(getApp(cluster, namespace, releaseName));
if (
app &&
app.chart &&
Expand All @@ -188,14 +190,15 @@ export function getAppWithUpdateInfo(
}

export function deleteApp(
cluster: string,
namespace: string,
releaseName: string,
purge: boolean,
): ThunkAction<Promise<boolean>, IStoreState, null, AppsAction> {
return async dispatch => {
dispatch(requestDeleteApp());
try {
await App.delete(namespace, releaseName, purge);
await App.delete(cluster, namespace, releaseName, purge);
dispatch(receiveDeleteApp());
return true;
} catch (e) {
Expand All @@ -207,6 +210,7 @@ export function deleteApp(

// fetchApps returns a list of apps for other actions to compose on top of it
export function fetchApps(
cluster: string,
ns?: string,
all: boolean = false,
): ThunkAction<Promise<IAppOverview[]>, IStoreState, null, AppsAction> {
Expand All @@ -216,7 +220,7 @@ export function fetchApps(
}
dispatch(listApps(all));
try {
const apps = await App.listApps(ns, all);
const apps = await App.listApps(cluster, ns, all);
dispatch(receiveAppList(apps));
return apps;
} catch (e) {
Expand All @@ -227,11 +231,12 @@ export function fetchApps(
}

export function fetchAppsWithUpdateInfo(
cluster: string,
namespace: string,
all: boolean = false,
): ThunkAction<Promise<void>, IStoreState, null, AppsAction> {
return async dispatch => {
const apps = await dispatch(fetchApps(namespace, all));
const apps = await dispatch(fetchApps(cluster, namespace, all));
apps.forEach(app =>
dispatch(
getAppUpdateInfo(
Expand Down Expand Up @@ -293,9 +298,10 @@ export function deployChart(
}

export function upgradeApp(
cluster: string,
namespace: string,
chartVersion: IChartVersion,
chartNamespace: string,
namespace: string,
releaseName: string,
values?: string,
schema?: JSONSchema4,
Expand All @@ -314,7 +320,7 @@ export function upgradeApp(
);
}
}
await App.upgrade(namespace, releaseName, chartNamespace, chartVersion, values);
await App.upgrade(cluster, namespace, releaseName, chartNamespace, chartVersion, values);
dispatch(receiveUpgradeApp());
return true;
} catch (e) {
Expand All @@ -325,16 +331,17 @@ export function upgradeApp(
}

export function rollbackApp(
cluster: string,
namespace: string,
releaseName: string,
revision: number,
): ThunkAction<Promise<boolean>, IStoreState, null, AppsAction> {
return async (dispatch, getState) => {
dispatch(requestRollbackApp());
try {
await App.rollback(namespace, releaseName, revision);
await App.rollback(cluster, namespace, releaseName, revision);
dispatch(receiveRollbackApp());
dispatch(getAppWithUpdateInfo(namespace, releaseName));
dispatch(getAppWithUpdateInfo(cluster, namespace, releaseName));
return true;
} catch (e) {
dispatch(errorApps(e));
Expand Down
4 changes: 2 additions & 2 deletions dashboard/src/components/AppList/AppList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ context("when changing props", () => {
/>,
);
wrapper.setProps({ namespace: "foo" });
expect(fetchAppsWithUpdateInfo).toHaveBeenCalledWith("foo", undefined);
expect(fetchAppsWithUpdateInfo).toHaveBeenCalledWith("defaultc", "foo", undefined);
expect(getCustomResources).toHaveBeenCalledWith("foo");
});

Expand Down Expand Up @@ -231,7 +231,7 @@ it("clicking 'List All' checkbox should trigger toggleListAll", () => {
checkbox.simulate("change");
// The last call to fetchApps should list all the apps
const fetchCalls = mockFetchAppsWithUpdateInfo.mock.calls;
expect(fetchCalls[fetchCalls.length - 1]).toEqual(["default", true]);
expect(fetchCalls[fetchCalls.length - 1]).toEqual(["defaultc", "default", true]);
});

it("renders the 'Show deleted apps' button even if the app list is empty", () => {
Expand Down
22 changes: 17 additions & 5 deletions dashboard/src/components/AppList/AppList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import CustomResourceListItem from "./CustomResourceListItem";

export interface IAppListProps {
apps: IAppState;
fetchAppsWithUpdateInfo: (ns: string, all: boolean) => void;
fetchAppsWithUpdateInfo: (cluster: string, ns: string, all: boolean) => void;
cluster: string;
namespace: string;
pushSearchFilter: (filter: string) => any;
Expand All @@ -34,8 +34,15 @@ interface IAppListState {
class AppList extends React.Component<IAppListProps, IAppListState> {
public state: IAppListState = { filter: "" };
public componentDidMount() {
const { fetchAppsWithUpdateInfo, filter, namespace, apps, getCustomResources } = this.props;
fetchAppsWithUpdateInfo(namespace, apps.listingAll);
const {
fetchAppsWithUpdateInfo,
filter,
cluster,
namespace,
apps,
getCustomResources,
} = this.props;
fetchAppsWithUpdateInfo(cluster, namespace, apps.listingAll);
if (this.props.featureFlags.operators) {
getCustomResources(namespace);
}
Expand All @@ -48,11 +55,12 @@ class AppList extends React.Component<IAppListProps, IAppListState> {
fetchAppsWithUpdateInfo,
getCustomResources,
filter,
cluster,
namespace,
} = this.props;
// refetch if new namespace or error removed due to location change
if (prevProps.namespace !== namespace || (!error && prevProps.apps.error)) {
fetchAppsWithUpdateInfo(namespace, listingAll);
fetchAppsWithUpdateInfo(cluster, namespace, listingAll);
if (this.props.featureFlags.operators) {
getCustomResources(namespace);
}
Expand Down Expand Up @@ -164,7 +172,11 @@ class AppList extends React.Component<IAppListProps, IAppListState> {
}

private toggleListAll = () => {
this.props.fetchAppsWithUpdateInfo(this.props.namespace, !this.props.apps.listingAll);
this.props.fetchAppsWithUpdateInfo(
this.props.cluster,
this.props.namespace,
!this.props.apps.listingAll,
);
};

private filteredReleases(apps: IAppOverview[], filter: string) {
Expand Down
9 changes: 5 additions & 4 deletions dashboard/src/components/AppUpgrade/AppUpgrade.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ export interface IAppUpgradeProps {
selected: IChartState["selected"];
deployed: IChartState["deployed"];
upgradeApp: (
cluster: string,
namespace: string,
version: IChartVersion,
chartNamespace: string,
namespace: string,
releaseName: string,
values?: string,
schema?: JSONSchema4,
) => Promise<boolean>;
fetchChartVersions: (namespace: string, id: string) => Promise<IChartVersion[]>;
getAppWithUpdateInfo: (namespace: string, releaseName: string) => void;
getAppWithUpdateInfo: (cluster: string, namespace: string, releaseName: string) => void;
getChartVersion: (namespace: string, id: string, chartVersion: string) => void;
getDeployedChartVersion: (namespace: string, id: string, chartVersion: string) => void;
push: (location: string) => RouterAction;
Expand All @@ -47,8 +48,8 @@ export interface IAppUpgradeProps {

class AppUpgrade extends React.Component<IAppUpgradeProps> {
public componentDidMount() {
const { releaseName, getAppWithUpdateInfo, namespace } = this.props;
getAppWithUpdateInfo(namespace, releaseName);
const { releaseName, getAppWithUpdateInfo, cluster, namespace } = this.props;
getAppWithUpdateInfo(cluster, namespace, releaseName);
}

public componentDidUpdate(prevProps: IAppUpgradeProps) {
Expand Down

0 comments on commit 2bfcb3a

Please sign in to comment.