Repository: awslabs/cdk8s
Pull request #351 (2020-10-19 14:53:36)

feat(lib): SecretValue
Introduce a struct called `SecretValue` which represents a value within a JSON secret. This allow encapsulating a specific secret value into a strongly-typed value.

Misc: make `resolve()` internal by moving to a non-exported file since jsii does not allow free floating functions.

BREAKING CHANGE: `EnvValue.fromSecret(secret, key)` has been removed in favor of `EnvValue.fromSecretValue({ secret, key })`.


mergify[bot]: Your pull request will be updated and merged automatically (do not update manually).

Patch:
diff --git a/examples/typescript/cdk8s-plus-elasticsearch-query/main.ts b/examples/typescript/cdk8s-plus-elasticsearch-query/main.ts
index c8e6b693a..4e4b13e4d 100644
--- a/examples/typescript/cdk8s-plus-elasticsearch-query/main.ts
+++ b/examples/typescript/cdk8s-plus-elasticsearch-query/main.ts
@@ -53,7 +53,7 @@ export class MyChart extends Chart {
       env: {
         ELASTIC_USERNAME: kplus.EnvValue.fromValue('elastic'),
         ELASTIC_ENDPOINT: kplus.EnvValue.fromValue(`http://${elastic.name}-es-http:${esPort}`),
-        ELASTIC_PASSWORD: kplus.EnvValue.fromSecret(passwordSecret, 'elastic')
+        ELASTIC_PASSWORD: kplus.EnvValue.fromSecretValue({ secret: passwordSecret, key: 'elastic' })
       }
     })
 
diff --git a/packages/cdk8s-plus/API.md b/packages/cdk8s-plus/API.md
index 508526383..142a1f050 100644
--- a/packages/cdk8s-plus/API.md
+++ b/packages/cdk8s-plus/API.md
@@ -51,6 +51,7 @@ Name|Description
 [ProbeOptions](#cdk8s-plus-probeoptions)|Probe options.
 [ResourceProps](#cdk8s-plus-resourceprops)|Initialization properties for resources.
 [SecretProps](#cdk8s-plus-secretprops)|*No description*
+[SecretValue](#cdk8s-plus-secretvalue)|Represents a specific value in JSON secret.
 [ServiceAccountProps](#cdk8s-plus-serviceaccountprops)|Properties for initialization of `ServiceAccount`.
 [ServiceIngressBackendOptions](#cdk8s-plus-serviceingressbackendoptions)|Options for setting up backends for ingress rules.
 [ServicePort](#cdk8s-plus-serviceport)|Definition of a service port.
@@ -662,17 +663,16 @@ static fromProcess(key: string, options?: EnvValueFromProcessOptions): EnvValue
 __Returns__:
 * <code>[EnvValue](#cdk8s-plus-envvalue)</code>
 
-#### *static* fromSecret(secret, key, options?)🔹 <a id="cdk8s-plus-envvalue-fromsecret"></a>
+#### *static* fromSecretValue(secretValue, options?)🔹 <a id="cdk8s-plus-envvalue-fromsecretvalue"></a>
 
-Create a by reading a specific key inside a secret.
+Defines an environment value from a secret JSON value.
 
 ```ts
-static fromSecret(secret: ISecret, key: string, options?: EnvValueFromSecretOptions): EnvValue
+static fromSecretValue(secretValue: SecretValue, options?: EnvValueFromSecretOptions): EnvValue
 ```
 
-* **secret** (<code>[ISecret](#cdk8s-plus-isecret)</code>)  - The secret.
-* **key** (<code>string</code>)  - The key.
-* **options** (<code>[EnvValueFromSecretOptions](#cdk8s-plus-envvaluefromsecretoptions)</code>)  - Additional options.
+* **secretValue** (<code>[SecretValue](#cdk8s-plus-secretvalue)</code>)  The secret value (secrent + key).
+* **options** (<code>[EnvValueFromSecretOptions](#cdk8s-plus-envvaluefromsecretoptions)</code>)  Additional options.
   * **optional** (<code>boolean</code>)  Specify whether the Secret or its key must be defined. __*Default*__: false
 
 __Returns__:
@@ -2253,6 +2253,20 @@ Name | Type | Description
 
 
 
+## struct SecretValue 🔹 <a id="cdk8s-plus-secretvalue"></a>
+
+
+Represents a specific value in JSON secret.
+
+
+
+Name | Type | Description 
+-----|------|-------------
+**key**🔹 | <code>string</code> | The JSON key.
+**secret**🔹 | <code>[ISecret](#cdk8s-plus-isecret)</code> | The secret.
+
+
+
 ## struct ServiceAccountProps 🔹 <a id="cdk8s-plus-serviceaccountprops"></a>
 
 
diff --git a/packages/cdk8s-plus/src/container.ts b/packages/cdk8s-plus/src/container.ts
index 17a13e2d5..1226d94cb 100644
--- a/packages/cdk8s-plus/src/container.ts
+++ b/packages/cdk8s-plus/src/container.ts
@@ -1,5 +1,5 @@
 import { IConfigMap } from './config-map';
-import { ISecret } from './secret';
+import { SecretValue } from './secret';
 import * as k8s from './imports/k8s';
 import { Volume } from './volume';
 import { Probe } from './probe';
@@ -71,18 +71,16 @@ export class EnvValue {
   }
 
   /**
-   * Create a by reading a specific key inside a secret.
-   *
-   * @param secret - The secret.
-   * @param key - The key.
-   * @param options - Additional options.
+   * Defines an environment value from a secret JSON value.
+   * 
+   * @param secretValue The secret value (secrent + key)
+   * @param options Additional options
    */
-  public static fromSecret(secret: ISecret, key: string, options: EnvValueFromSecretOptions = {}): EnvValue {
-
+  public static fromSecretValue(secretValue: SecretValue, options: EnvValueFromSecretOptions = {}): EnvValue {
     const source: k8s.EnvVarSource = {
       secretKeyRef: {
-        name: secret.name,
-        key,
+        name: secretValue.secret.name,
+        key: secretValue.key,
         optional: options.optional,
       },
     };
diff --git a/packages/cdk8s-plus/src/secret.ts b/packages/cdk8s-plus/src/secret.ts
index fd133dc93..4d8fc4cb8 100644
--- a/packages/cdk8s-plus/src/secret.ts
+++ b/packages/cdk8s-plus/src/secret.ts
@@ -17,6 +17,21 @@ export interface ISecret extends IResource {
 
 }
 
+/**
+ * Represents a specific value in JSON secret.
+ */
+export interface SecretValue {
+  /**
+   * The secret
+   */
+  readonly secret: ISecret;
+
+  /**
+   * The JSON key
+   */
+  readonly key: string;
+}
+
 /**
  * Kubernetes Secrets let you store and manage sensitive information, such as
  * passwords, OAuth tokens, and ssh keys. Storing confidential information in a
diff --git a/packages/cdk8s/src/_resolve.ts b/packages/cdk8s/src/_resolve.ts
new file mode 100644
index 000000000..f301e5baa
--- /dev/null
+++ b/packages/cdk8s/src/_resolve.ts
@@ -0,0 +1,37 @@
+import { Lazy } from './lazy';
+
+export function resolve(value: any): any {
+
+  if (value == null) {
+    return value;
+  }
+
+  // cdk8s token
+  if (value instanceof Lazy) {
+    const resolved = value.produce();
+    return resolve(resolved);
+  }
+
+  // implicit token as generated by the cdk8s-cli (e.g IntOrString)
+  if (typeof(value.resolve) === 'function') {
+    const resolved = value.resolve();
+    return resolve(resolved);
+  }
+
+  if (typeof(value) !== 'object') {
+    return value;
+  }
+
+  if (Array.isArray(value)) {
+    return value.map(x => resolve(x));
+  }
+
+  const result: any = {};
+
+  for (const [k,v] of Object.entries(value)) {
+    result[k] = resolve(v);
+  }
+
+  return result;
+
+}
diff --git a/packages/cdk8s/src/api-object.ts b/packages/cdk8s/src/api-object.ts
index b05533cb3..174508858 100644
--- a/packages/cdk8s/src/api-object.ts
+++ b/packages/cdk8s/src/api-object.ts
@@ -1,7 +1,7 @@
 import { Construct, IConstruct, Node } from 'constructs';
 import { Chart } from './chart';
 import { sanitizeValue } from './_util';
-import { resolve } from './lazy';
+import { resolve } from './_resolve';
 import * as stringify from 'json-stable-stringify';
 import { ApiObjectMetadata, ApiObjectMetadataDefinition } from './metadata';
 
diff --git a/packages/cdk8s/src/lazy.ts b/packages/cdk8s/src/lazy.ts
index c434b18ec..ca46b692e 100644
--- a/packages/cdk8s/src/lazy.ts
+++ b/packages/cdk8s/src/lazy.ts
@@ -16,39 +16,4 @@ export interface IAnyProducer {
   produce(): any;
 }
 
-export function resolve(value: any): any {
-
-  if (value == null) {
-    return value;
-  }
-
-  // cdk8s token
-  if (value instanceof Lazy) {
-    const resolved = value.produce();
-    return resolve(resolved);
-  }
-
-  // implicit token as generated by the cdk8s-cli (e.g IntOrString)
-  if (typeof(value.resolve) === 'function') {
-    const resolved = value.resolve();
-    return resolve(resolved);
-  }
-
-  if (typeof(value) !== 'object') {
-    return value;
-  }
-
-  if (Array.isArray(value)) {
-    return value.map(x => resolve(x));
-  }
-
-  const result: any = {};
-
-  for (const [k,v] of Object.entries(value)) {
-    result[k] = resolve(v);
-  }
-
-  return result;
-
-}
 


Test patch:
diff --git a/packages/cdk8s-plus/test/container.test.ts b/packages/cdk8s-plus/test/container.test.ts
index 3327a6f3e..0f70a8acf 100644
--- a/packages/cdk8s-plus/test/container.test.ts
+++ b/packages/cdk8s-plus/test/container.test.ts
@@ -27,18 +27,21 @@ describe('EnvValue', () => {
 
   });
 
-  test('Can be created from secret', () => {
-
-    const actual = kplus.EnvValue.fromSecret(kplus.Secret.fromSecretName('Secret'), 'key');
+  test('Can be created from secret value', () => {
+    const secretValue = {
+      secret: kplus.Secret.fromSecretName('Secret'),
+      key: 'my-key',
+    }
+    
+    const actual = kplus.EnvValue.fromSecretValue(secretValue);
 
     expect(actual.value).toBeUndefined();
     expect(actual.valueFrom).toEqual({
       secretKeyRef: {
-        key: 'key',
+        key: 'my-key',
         name: 'Secret',
       },
     });
-
   });
 
   test('Cannot be created from missing required process env', () => {
diff --git a/packages/cdk8s/test/tokens.test.ts b/packages/cdk8s/test/tokens.test.ts
index c46f97c9d..dfc3bbbc4 100644
--- a/packages/cdk8s/test/tokens.test.ts
+++ b/packages/cdk8s/test/tokens.test.ts
@@ -1,6 +1,5 @@
 import { ApiObject, Lazy, Testing } from '../src';
-import { resolve } from '../src/lazy';
-
+import { resolve } from '../src/_resolve';
 
 test('lazy', () => {
   // GIVEN