-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2312 from headlamp-k8s/apiproxy-refactor
frontend: Refactor apiProxy
- Loading branch information
Showing
21 changed files
with
2,241 additions
and
2,009 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import _ from 'lodash'; | ||
import { getCluster } from '../../cluster'; | ||
import { KubeObjectInterface } from '../cluster'; | ||
import { getClusterDefaultNamespace } from './clusterApi'; | ||
import { ApiError } from './clusterRequests'; | ||
import { resourceDefToApiFactory } from './factories'; | ||
|
||
/** | ||
* Applies the provided body to the Kubernetes API. | ||
* | ||
* Tries to POST, and if there's a conflict it does a PUT to the api endpoint. | ||
* | ||
* @param body - The kubernetes object body to apply. | ||
* @param clusterName - The cluster to apply the body to. By default uses the current cluster (URL defined). | ||
* | ||
* @returns The response from the kubernetes API server. | ||
*/ | ||
export async function apply<T extends KubeObjectInterface>( | ||
body: T, | ||
clusterName?: string | ||
): Promise<T> { | ||
const bodyToApply = _.cloneDeep(body); | ||
|
||
let apiEndpoint; | ||
try { | ||
apiEndpoint = await resourceDefToApiFactory(bodyToApply, clusterName); | ||
} catch (err) { | ||
console.error(`Error getting api endpoint when applying the resource ${bodyToApply}: ${err}`); | ||
throw err; | ||
} | ||
|
||
const cluster = clusterName || getCluster(); | ||
|
||
// Check if the default namespace is needed. And we need to do this before | ||
// getting the apiEndpoint because it will affect the endpoint itself. | ||
const isNamespaced = apiEndpoint.isNamespaced; | ||
const { namespace } = body.metadata; | ||
if (!namespace && isNamespaced) { | ||
let defaultNamespace = 'default'; | ||
|
||
if (!!cluster) { | ||
defaultNamespace = getClusterDefaultNamespace(cluster) || 'default'; | ||
} | ||
|
||
bodyToApply.metadata.namespace = defaultNamespace; | ||
} | ||
|
||
const resourceVersion = bodyToApply.metadata.resourceVersion; | ||
|
||
try { | ||
delete bodyToApply.metadata.resourceVersion; | ||
return await apiEndpoint.post(bodyToApply, {}, cluster!); | ||
} catch (err) { | ||
// Check to see if failed because the record already exists. | ||
// If the failure isn't a 409 (i.e. Confilct), just rethrow. | ||
if ((err as ApiError).status !== 409) throw err; | ||
|
||
// Preserve the resourceVersion if its an update request | ||
bodyToApply.metadata.resourceVersion = resourceVersion; | ||
// We had a conflict. Try a PUT | ||
return apiEndpoint.put(bodyToApply, {}, cluster!) as Promise<T>; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
import helpers, { getHeadlampAPIHeaders } from '../../../helpers'; | ||
import { ConfigState } from '../../../redux/configSlice'; | ||
import store from '../../../redux/stores/store'; | ||
import { | ||
deleteClusterKubeconfig, | ||
findKubeconfigByClusterName, | ||
storeStatelessClusterKubeconfig, | ||
} from '../../../stateless'; | ||
import { getCluster } from '../../util'; | ||
import { ClusterRequest, clusterRequest, post, request } from './clusterRequests'; | ||
import { JSON_HEADERS } from './constants'; | ||
|
||
/** | ||
* Test authentication for the given cluster. | ||
* Will throw an error if the user is not authenticated. | ||
*/ | ||
export async function testAuth(cluster = '', namespace = 'default') { | ||
const spec = { namespace }; | ||
const clusterName = cluster || getCluster(); | ||
|
||
return post('/apis/authorization.k8s.io/v1/selfsubjectrulesreviews', { spec }, false, { | ||
timeout: 5 * 1000, | ||
cluster: clusterName, | ||
}); | ||
} | ||
|
||
/** | ||
* Checks cluster health | ||
* Will throw an error if the cluster is not healthy. | ||
*/ | ||
export async function testClusterHealth(cluster?: string) { | ||
const clusterName = cluster || getCluster() || ''; | ||
return clusterRequest('/healthz', { isJSON: false, cluster: clusterName }); | ||
} | ||
|
||
export async function setCluster(clusterReq: ClusterRequest) { | ||
const kubeconfig = clusterReq.kubeconfig; | ||
|
||
if (kubeconfig) { | ||
await storeStatelessClusterKubeconfig(kubeconfig); | ||
// We just send parsed kubeconfig from the backend to the frontend. | ||
return request( | ||
'/parseKubeConfig', | ||
{ | ||
method: 'POST', | ||
body: JSON.stringify(clusterReq), | ||
headers: { | ||
...JSON_HEADERS, | ||
}, | ||
}, | ||
false, | ||
false | ||
); | ||
} | ||
|
||
return request( | ||
'/cluster', | ||
{ | ||
method: 'POST', | ||
body: JSON.stringify(clusterReq), | ||
headers: { | ||
...JSON_HEADERS, | ||
...getHeadlampAPIHeaders(), | ||
}, | ||
}, | ||
false, | ||
false | ||
); | ||
} | ||
|
||
// @todo: needs documenting. | ||
|
||
export async function deleteCluster( | ||
cluster: string | ||
): Promise<{ clusters: ConfigState['clusters'] }> { | ||
if (cluster) { | ||
const kubeconfig = await findKubeconfigByClusterName(cluster); | ||
if (kubeconfig !== null) { | ||
await deleteClusterKubeconfig(cluster); | ||
window.location.reload(); | ||
return { clusters: {} }; | ||
} | ||
} | ||
|
||
return request( | ||
`/cluster/${cluster}`, | ||
{ method: 'DELETE', headers: { ...getHeadlampAPIHeaders() } }, | ||
false, | ||
false | ||
); | ||
} | ||
|
||
/** | ||
* getClusterDefaultNamespace gives the default namespace for the given cluster. | ||
* | ||
* If the checkSettings parameter is true (default), it will check the cluster settings first. | ||
* Otherwise it will just check the cluster config. This means that if one needs the default | ||
* namespace that may come from the kubeconfig, call this function with the checkSettings parameter as false. | ||
* | ||
* @param cluster The cluster name. | ||
* @param checkSettings Whether to check the settings for the default namespace (otherwise it just checks the cluster config). Defaults to true. | ||
* | ||
* @returns The default namespace for the given cluster. | ||
*/ | ||
export function getClusterDefaultNamespace(cluster: string, checkSettings?: boolean): string { | ||
const includeSettings = checkSettings ?? true; | ||
let defaultNamespace = ''; | ||
|
||
if (!!cluster) { | ||
if (includeSettings) { | ||
const clusterSettings = helpers.loadClusterSettings(cluster); | ||
defaultNamespace = clusterSettings?.defaultNamespace || ''; | ||
} | ||
|
||
if (!defaultNamespace) { | ||
const state = store.getState(); | ||
const clusterDefaultNs: string = | ||
state.config?.clusters?.[cluster]?.meta_data?.namespace || ''; | ||
defaultNamespace = clusterDefaultNs; | ||
} | ||
} | ||
|
||
return defaultNamespace; | ||
} | ||
|
||
/** | ||
* renameCluster sends call to backend to update a field in kubeconfig which | ||
* is the custom name of the cluster used by the user. | ||
* @param cluster | ||
*/ | ||
export async function renameCluster(cluster: string, newClusterName: string, source: string) { | ||
let stateless = false; | ||
if (cluster) { | ||
const kubeconfig = await findKubeconfigByClusterName(cluster); | ||
if (kubeconfig !== null) { | ||
stateless = true; | ||
} | ||
} | ||
|
||
return request( | ||
`/cluster/${cluster}`, | ||
{ | ||
method: 'PUT', | ||
headers: { ...getHeadlampAPIHeaders() }, | ||
body: JSON.stringify({ newClusterName, source, stateless }), | ||
}, | ||
false, | ||
false | ||
); | ||
} | ||
|
||
/** | ||
* parseKubeConfig sends call to backend to parse kubeconfig and send back | ||
* the parsed clusters and contexts. | ||
* @param clusterReq - The cluster request object. | ||
*/ | ||
export async function parseKubeConfig(clusterReq: ClusterRequest) { | ||
const kubeconfig = clusterReq.kubeconfig; | ||
|
||
if (kubeconfig) { | ||
return request( | ||
'/parseKubeConfig', | ||
{ | ||
method: 'POST', | ||
body: JSON.stringify(clusterReq), | ||
headers: { | ||
...JSON_HEADERS, | ||
...getHeadlampAPIHeaders(), | ||
}, | ||
}, | ||
false, | ||
false | ||
); | ||
} | ||
|
||
return null; | ||
} |
Oops, something went wrong.