-
Notifications
You must be signed in to change notification settings - Fork 273
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 #82 from garden-io/login-command
Login command
- Loading branch information
Showing
32 changed files
with
3,298 additions
and
2,689 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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,28 @@ | ||
/* | ||
* Copyright (C) 2018 Garden Technologies, Inc. <[email protected]> | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
|
||
import { Command } from "./base" | ||
import { EntryStyle } from "../logger/types" | ||
import { PluginContext } from "../plugin-context" | ||
import { LoginStatusMap } from "../types/plugin" | ||
|
||
export class LoginCommand extends Command { | ||
name = "login" | ||
help = "Log into the Garden framework" | ||
|
||
async action(ctx: PluginContext): Promise<LoginStatusMap> { | ||
ctx.log.header({ emoji: "unlock", command: "Login" }) | ||
ctx.log.info({ msg: "Logging in...", entryStyle: EntryStyle.activity }) | ||
|
||
const result = await ctx.login() | ||
|
||
ctx.log.info("\nLogin success!") | ||
|
||
return result | ||
} | ||
} |
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,30 @@ | ||
/* | ||
* Copyright (C) 2018 Garden Technologies, Inc. <[email protected]> | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
|
||
import { Command } from "./base" | ||
import { EntryStyle } from "../logger/types" | ||
import { PluginContext } from "../plugin-context" | ||
import { LoginStatusMap } from "../types/plugin" | ||
|
||
export class LogoutCommand extends Command { | ||
name = "logout" | ||
help = "Log into the Garden framework" | ||
|
||
async action(ctx: PluginContext): Promise<LoginStatusMap> { | ||
|
||
ctx.log.header({ emoji: "lock", command: "Logout" }) | ||
|
||
const entry = ctx.log.info({ msg: "Logging out...", entryStyle: EntryStyle.activity }) | ||
|
||
const result = await ctx.logout() | ||
|
||
entry.setSuccess("Logged out successfully") | ||
|
||
return result | ||
} | ||
} |
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,194 @@ | ||
/* | ||
* Copyright (C) 2018 Garden Technologies, Inc. <[email protected]> | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
|
||
import { resolve } from "path" | ||
import { ensureFile, readFile } from "fs-extra" | ||
import * as Joi from "joi" | ||
import * as yaml from "js-yaml" | ||
import { get, isPlainObject, unset } from "lodash" | ||
import { joiIdentifier, Primitive, validate } from "./types/common" | ||
import { LocalConfigError } from "./exceptions" | ||
import { dumpYaml } from "./util" | ||
|
||
export type ConfigValue = Primitive | Primitive[] | ||
|
||
export type SetManyParam = { keyPath: Array<string>, value: ConfigValue }[] | ||
|
||
export abstract class ConfigStore<T extends object = any> { | ||
private cached: null | T | ||
protected configPath: string | ||
|
||
constructor(projectPath: string) { | ||
this.configPath = this.setConfigPath(projectPath) | ||
this.cached = null | ||
} | ||
|
||
abstract setConfigPath(projectPath: string): string | ||
abstract validate(config): T | ||
|
||
/** | ||
* Would've been nice to allow something like: set(["path", "to", "valA", valA], ["path", "to", "valB", valB]...) | ||
* but Typescript support is missing at the moment | ||
*/ | ||
public async set(param: SetManyParam) | ||
public async set(keyPath: string[], value: ConfigValue) | ||
public async set(...args) { | ||
let config = await this.getConfig() | ||
let entries: SetManyParam | ||
|
||
if (args.length === 1) { | ||
entries = args[0] | ||
} else { | ||
entries = [{ keyPath: args[0], value: args[1] }] | ||
} | ||
|
||
for (const { keyPath, value } of entries) { | ||
config = this.updateConfig(config, keyPath, value) | ||
} | ||
|
||
return this.saveLocalConfig(config) | ||
} | ||
|
||
public async get(): Promise<T> | ||
public async get(keyPath: string[]): Promise<Object | ConfigValue> | ||
public async get(keyPath?: string[]): Promise<Object | ConfigValue> { | ||
const config = await this.getConfig() | ||
|
||
if (keyPath) { | ||
const value = get(config, keyPath) | ||
|
||
if (value === undefined) { | ||
this.throwKeyNotFound(config, keyPath) | ||
} | ||
|
||
return value | ||
} | ||
|
||
return config | ||
} | ||
|
||
public async clear() { | ||
return this.saveLocalConfig(<T>{}) | ||
} | ||
|
||
public async delete(keyPath: string[]) { | ||
let config = await this.getConfig() | ||
if (get(config, keyPath) === undefined) { | ||
this.throwKeyNotFound(config, keyPath) | ||
} | ||
const success = unset(config, keyPath) | ||
if (!success) { | ||
throw new LocalConfigError(`Unable to delete key ${keyPath.join(".")} in user config`, { | ||
keyPath, | ||
config, | ||
}) | ||
} | ||
return this.saveLocalConfig(config) | ||
} | ||
|
||
private async getConfig(): Promise<T> { | ||
let config: T | ||
if (this.cached) { | ||
// Spreading does not work on generic types, see: https://github.com/Microsoft/TypeScript/issues/13557 | ||
config = Object.assign(this.cached, {}) | ||
} else { | ||
config = await this.loadConfig() | ||
} | ||
return config | ||
} | ||
|
||
private updateConfig(config: T, keyPath: string[], value: ConfigValue): T { | ||
let currentValue = config | ||
|
||
for (let i = 0; i < keyPath.length; i++) { | ||
const k = keyPath[i] | ||
|
||
if (i === keyPath.length - 1) { | ||
currentValue[k] = value | ||
} else if (currentValue[k] === undefined) { | ||
currentValue[k] = {} | ||
} else if (!isPlainObject(currentValue[k])) { | ||
const path = keyPath.slice(i + 1).join(".") | ||
|
||
throw new LocalConfigError( | ||
`Attempting to assign a nested key on non-object (current value at ${path}: ${currentValue[k]})`, | ||
{ | ||
currentValue: currentValue[k], | ||
path, | ||
}, | ||
) | ||
} | ||
|
||
currentValue = currentValue[k] | ||
} | ||
return config | ||
} | ||
|
||
private async ensureConfigFileExists() { | ||
return ensureFile(this.configPath) | ||
} | ||
|
||
private async loadConfig(): Promise<T> { | ||
await this.ensureConfigFileExists() | ||
const config = await yaml.safeLoad((await readFile(this.configPath)).toString()) || {} | ||
|
||
this.cached = this.validate(config) | ||
|
||
return this.cached | ||
} | ||
|
||
private async saveLocalConfig(config: T) { | ||
this.cached = null | ||
const validated = this.validate(config) | ||
await dumpYaml(this.configPath, validated) | ||
this.cached = config | ||
} | ||
|
||
private throwKeyNotFound(config: T, keyPath: string[]) { | ||
throw new LocalConfigError(`Could not find key ${keyPath.join(".")} in user config`, { | ||
keyPath, | ||
config, | ||
}) | ||
} | ||
|
||
} | ||
|
||
export interface KubernetesLocalConfig { | ||
username?: string | ||
"previous-usernames"?: Array<string> | ||
} | ||
|
||
export interface LocalConfig { | ||
kubernetes?: KubernetesLocalConfig | ||
} | ||
|
||
const kubernetesLocalConfigSchema = Joi.object().keys({ | ||
username: joiIdentifier().allow("").optional(), | ||
"previous-usernames": Joi.array().items(joiIdentifier()).optional(), | ||
}) | ||
|
||
// TODO: Dynamically populate schema with all possible provider keys? | ||
const localConfigSchema = Joi.object().keys({ | ||
kubernetes: kubernetesLocalConfigSchema, | ||
}) | ||
|
||
export class LocalConfigStore extends ConfigStore<LocalConfig> { | ||
|
||
setConfigPath(projectPath): string { | ||
return resolve(projectPath, ".garden", "local-config.yml") | ||
} | ||
|
||
validate(config): LocalConfig { | ||
return validate( | ||
config, | ||
localConfigSchema, | ||
{ context: this.configPath, ErrorClass: LocalConfigError }, | ||
) | ||
} | ||
|
||
} |
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
Oops, something went wrong.