Skip to content

Commit

Permalink
Add ability to import K8S endpoints from kube config file (#381)
Browse files Browse the repository at this point in the history
* Fix table passing issue

* WIP: Import from kube config file

* Minor tidy ups

* Remove debugging code

* Fix for error message being swallowed

* Improvements to load, add edit name
- tidied up selection component
- move non-ui logic into helper
- move record of clusters into helper
- tidied up config helper
- fixed select all intermediate state on bizare selections & first load
- cleans a lot of table datasource interfaces

* Specific fixes for upstream
- Fix table header alignment
- Fix checkbox table column alignment

* Rename name column component, fix default context selection when invalid

* Tidying up, add skip ssl, fix register of new

* Allow user to skip connect by not suppling user

* Tidying up, set AZK type, fixes

* Minor fixes, subtle edit symbols

* Fix case where cluster is register only but cannot connect, allow user to review final step

* Add detection for EKS

* Remove border between row that's errored and it's errror description row

* Fix unit tests

* Set initial state of skip ssl checkbox given request to kube

* Fix unit tests

* Remove some console.logs

* Multiple small changes/fixes
- tidy up
- remove /kubeconfig.yaml fetch
- fix select all at indeterminate state (should always select all)
- fix apply for auto skip ssl

* Fix connect error status message, change title of register endpoint stepper, minor changes

* Improve table row error
- remove border radius after expansion change
- error rows have large indent to help show associated row
- remove old styles that weren't applied following expansion change

* Spacing

* Changes following review

* Changes following review

* Fix build warning which is silent in dev world

* Fixes following merge

* Fix e2e test

* Add icon for kube config import

Co-authored-by: Richard Cox <richard.cox@suse.com>
  • Loading branch information
nwmac and richard-cox committed Jun 26, 2020
1 parent d64f5bf commit 37f53d5
Show file tree
Hide file tree
Showing 80 changed files with 1,963 additions and 118 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { ComponentFactoryResolver, Injector } from '@angular/core';
import { FormBuilder } from '@angular/forms';

import { entityCatalog } from '../../../../../store/src/entity-catalog/entity-catalog';
import { ConnectEndpointData } from '../../../features/endpoints/connect.service';
import { RowState } from '../../../shared/components/list/data-sources-controllers/list-data-source-types';
import { KUBERNETES_ENDPOINT_TYPE } from '../kubernetes-entity-factory';
import { EndpointAuthTypeConfig, IAuthForm } from './../../../core/extension/extension-types';
import { KubeConfigFileCluster, KubeConfigFileUser } from './kube-config.types';

/**
* Auth helper tries to figure out the Kubernetes sub-type and auth to use
* based on the kube config file contents
*/
export class KubeConfigAuthHelper {

authTypes: { [name: string]: EndpointAuthTypeConfig } = {};

public subTypes = [];

constructor() {
const epTypeInfo = entityCatalog.getAllEndpointTypes(false);
const k8s = epTypeInfo.find(entity => entity.type === KUBERNETES_ENDPOINT_TYPE);
if (k8s && k8s.definition) {
const defn = k8s.definition;

// Collect all of the auth types
defn.authTypes.forEach(at => {
this.authTypes[at.value] = at;
});

this.subTypes.push({ id: '', name: 'Generic' });

// Collect all of the auth types for the sub-types
defn.subTypes.forEach(st => {
if (st.type !== 'config') {
this.subTypes.push({ id: st.type, name: st.labelShort });
}
st.authTypes.forEach(at => {
this.authTypes[at.value] = at;
});
});

// Sort the subtypes
this.subTypes = this.subTypes.sort((a, b) => a.name.localeCompare(b.name));
}
}

// Try and parse the authentication metadata
public parseAuth(cluster: KubeConfigFileCluster, user: KubeConfigFileUser): RowState {

// Default subtype is generic Kubernetes
cluster._subType = '';

// Certificate authentication first

// In-file certificate authentication
if (user.user['client-certificate-data'] && user.user['client-key-data']) {
// We are good to go - create the form data

// Default is generic kubernetes
let subType = '';
const authType = 'kube-cert-auth';
if (cluster.cluster.server.indexOf('azmk8s.io') >= 0) {
// Probably Azure
subType = 'aks';
cluster._subType = 'aks';
}

const authData = {
authType,
subType,
values: {
cert: user.user['client-certificate-data'],
certKey: user.user['client-key-data']
}
};
user._authData = authData;
return {};
}

if (user.user['client-certificate'] || user.user['client-key']) {
cluster._additionalUserInfo = true;
return {
message: 'This endpoint will be registered but not connected (additional information is required)',
info: true
};
}

const authProvider = user.user['auth-provider'];


if (authProvider && authProvider.config) {
if (authProvider.config['cmd-path'] && authProvider.config['cmd-path'].indexOf('gcloud') !== -1) {
// GKE
cluster._subType = 'gke';
// Can not connect to GKE - user must do so manually
cluster._additionalUserInfo = true;
return {
message: 'This endpoint will be registered but not connected (additional information is required)',
info: true
};
}
}

if (
cluster.cluster.server.indexOf('eks.amazonaws.com') >= 0 ||
(user.user.exec && user.user.exec.command && user.user.exec.command === 'aws-iam-authenticator')
) {
// Probably EKS
cluster._subType = 'eks';
cluster._additionalUserInfo = true;
return {
message: 'This endpoint will be registered but not connected (additional information is required)',
info: true
};
}

return { message: 'Authentication mechanism is not supported', warning: true };
}

// Use the auto component to get the data in the correct format for connecting to the endpoint
public getAuthDataForConnect(resolver: ComponentFactoryResolver, injector: Injector, fb: FormBuilder, user: KubeConfigFileUser)
: ConnectEndpointData | null {

let data = null;

// Get the component to us
if (user && user._authData) {
const authType = this.authTypes[user._authData.authType];

const factory = resolver.resolveComponentFactory<IAuthForm>(authType.component);

const ref = factory.create(injector);

const form = fb.group({
authType: authType.value,
systemShared: false,
authValues: fb.group(user._authData.values)
});

ref.instance.formGroup = form;

// Allow the auth form to supply body content if it needs to
const endpointFormInstance = ref.instance as any;
if (endpointFormInstance.getBody && endpointFormInstance.getValues) {
data = {
authType: authType.value,
authVal: endpointFormInstance.getValues(user._authData.values),
systemShared: false,
bodyContent: endpointFormInstance.getBody()
};
}
ref.destroy();
}
return data;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="kubeconfig-import">
<app-table [dataSource]="dataSource" [columns]="columns"></app-table>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
:host {
display: flex;
flex: 1;
}

.kubeconfig-import {
flex: 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { KubernetesBaseTestModules } from '../../kubernetes.testing.module';
import { KubeConfigImportComponent } from './kube-config-import.component';

describe('KubeConfigImportComponent', () => {
let component: KubeConfigImportComponent;
let fixture: ComponentFixture<KubeConfigImportComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
...KubernetesBaseTestModules
],
declarations: [KubeConfigImportComponent]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(KubeConfigImportComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading

0 comments on commit 37f53d5

Please sign in to comment.