Repository: iterative/cml (js)

Issue:
Support Instance roles - permissions
> Some applications rely on a "trusted agent" (to steal aws' terminology) system to determine their access to other systems.


Allow the specification of this role/service-account for the instance to be created as.
- AWS `instance-profile`
- GCP `service account email`
- Azure `system-assigned managed identity` ? (I haven't used enough of azure)

------
Some quick research shows this to be supported by all of the cloud providers supported so far
### AWS
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html

### GCP
https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances

### Azure
https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm?context=/azure/virtual-machines/context/context

Pull request description:
Implement instance permissions/role
 - Closes #773 

I can close the draft if preferred, but I figured I'd put it up for the sake of visibility.

⚠️ **Requires upstream changes**
- will rebase out testing changes

Patch:
diff --git a/bin/cml/runner.js b/bin/cml/runner.js
index 326dadb..fca7b63 100755
--- a/bin/cml/runner.js
+++ b/bin/cml/runner.js
@@ -118,6 +118,7 @@ const runCloud = async (opts) => {
       cloud,
       cloudRegion: region,
       cloudType: type,
+      cloudPermissionSet: permissionSet,
       cloudMetadata: metadata,
       cloudGpu: gpu,
       cloudHddSize: hddSize,
@@ -152,6 +153,7 @@ const runCloud = async (opts) => {
         cloud,
         region,
         type,
+        permissionSet,
         metadata,
         gpu: gpu === 'tesla' ? 'v100' : gpu,
         hddSize,
@@ -191,6 +193,7 @@ const runCloud = async (opts) => {
           instanceIp: attributes.instance_ip,
           instanceLaunchTime: attributes.instance_launch_time,
           instanceType: attributes.instance_type,
+          instancePermissionSet: attributes.instance_permission_set,
           labels: attributes.labels,
           metadata: attributes.metadata,
           name: attributes.name,
@@ -470,6 +473,12 @@ exports.builder = (yargs) =>
         description:
           'Instance type. Choices: [m, l, xl]. Also supports native types like i.e. t2.micro'
       },
+      cloudPermissionSet: {
+        type: 'string',
+        default: '',
+        description:
+          'Specifies the instance profile in AWS or instance service account in GCP'
+      },
       cloudMetadata: {
         type: 'array',
         string: true,
diff --git a/src/terraform.js b/src/terraform.js
index 91fdb16..cf0b9ba 100644
--- a/src/terraform.js
+++ b/src/terraform.js
@@ -77,6 +77,7 @@ const iterativeCmlRunnerTpl = (opts = {}) => {
     name,
     single,
     type,
+    permissionSet,
     metadata,
     gpu,
     hddSize,
@@ -107,6 +108,7 @@ resource "iterative_cml_runner" "runner" {
   ${type ? `instance_type = "${type}"` : ''}
   ${gpu ? `instance_gpu = "${gpu}"` : ''}
   ${hddSize ? `instance_hdd_size = ${hddSize}` : ''}
+  ${permissionSet ? `instance_permission_set = "${permissionSet}"` : ''}
   ${sshPrivate ? `ssh_private = "${sshPrivate}"` : ''}
   ${spot ? `spot = ${spot}` : ''}
   ${spotPrice ? `spot_price = ${spotPrice}` : ''}


Test patch:
diff --git a/bin/cml/runner.test.js b/bin/cml/runner.test.js
index 727317e..d928956 100644
--- a/bin/cml/runner.test.js
+++ b/bin/cml/runner.test.js
@@ -42,6 +42,8 @@ Options:
                                                    [string] [default: \\"us-west\\"]
   --cloud-type                Instance type. Choices: [m, l, xl]. Also supports
                               native types like i.e. t2.micro           [string]
+  --cloud-permission-set      Specifies the instance profile in AWS or instance
+                              service account in GCP      [string] [default: \\"\\"]
   --cloud-metadata            Key Value pairs to associate cml-runner instance
                               on the provider i.e. tags/labels \\"key=value\\"
                                                            [array] [default: []]
diff --git a/src/terraform.test.js b/src/terraform.test.js
index 3fb7a47..718d7c4 100644
--- a/src/terraform.test.js
+++ b/src/terraform.test.js
@@ -36,6 +36,7 @@ describe('Terraform tests', () => {
         
         
         
+        
       }
       "
     `);
@@ -87,6 +88,7 @@ resource \\"iterative_cml_runner\\" \\"runner\\" {
   instance_type = \\"mymachinetype\\"
   instance_gpu = \\"mygputype\\"
   instance_hdd_size = 50
+  
   ssh_private = \\"myprivate\\"
   spot = true
   spot_price = 0.0001
@@ -143,6 +145,7 @@ resource \\"iterative_cml_runner\\" \\"runner\\" {
   instance_type = \\"mymachinetype\\"
   instance_gpu = \\"mygputype\\"
   instance_hdd_size = 50
+  
   ssh_private = \\"myprivate\\"
   spot = true
   spot_price = 0.0001
@@ -200,6 +203,7 @@ resource \\"iterative_cml_runner\\" \\"runner\\" {
   instance_type = \\"mymachinetype\\"
   instance_gpu = \\"mygputype\\"
   instance_hdd_size = 50
+  
   ssh_private = \\"myprivate\\"
   spot = true
   spot_price = 0.0001
@@ -214,6 +218,65 @@ resource \\"iterative_cml_runner\\" \\"runner\\" {
 `);
   });
 
+  test('basic settings with permission set', async () => {
+    const output = iterativeCmlRunnerTpl({
+      repo: 'https://',
+      token: 'abc',
+      driver: 'gitlab',
+      labels: 'mylabel',
+      idleTimeout: 300,
+      name: 'myrunner',
+      single: true,
+      cloud: 'aws',
+      region: 'west',
+      type: 'mymachinetype',
+      permissionSet: 'arn:aws:iam::1:instance-profile/x',
+      gpu: 'mygputype',
+      hddSize: 50,
+      sshPrivate: 'myprivate',
+      spot: true,
+      spotPrice: '0.0001',
+      awsSecurityGroup: 'mysg'
+    });
+    expect(output).toMatchInlineSnapshot(`
+"
+
+terraform {
+  required_providers {
+    iterative = {
+      source = \\"iterative/iterative\\"
+    }
+  }
+}
+
+provider \\"iterative\\" {}
+
+
+resource \\"iterative_cml_runner\\" \\"runner\\" {
+  repo = \\"https://\\"
+  token = \\"abc\\"
+  driver = \\"gitlab\\"
+  labels = \\"mylabel\\"
+  idle_timeout = 300
+  name = \\"myrunner\\"
+  single = \\"true\\"
+  cloud = \\"aws\\"
+  region = \\"west\\"
+  instance_type = \\"mymachinetype\\"
+  instance_gpu = \\"mygputype\\"
+  instance_hdd_size = 50
+  instance_permission_set = \\"arn:aws:iam::1:instance-profile/x\\"
+  ssh_private = \\"myprivate\\"
+  spot = true
+  spot_price = 0.0001
+  
+  aws_security_group = \\"mysg\\"
+  
+}
+"
+`);
+  });
+
   test('Startup script', async () => {
     const output = iterativeCmlRunnerTpl({
       repo: 'https://',
@@ -260,6 +323,7 @@ resource \\"iterative_cml_runner\\" \\"runner\\" {
   instance_type = \\"mymachinetype\\"
   instance_gpu = \\"mygputype\\"
   instance_hdd_size = 50
+  
   ssh_private = \\"myprivate\\"
   spot = true
   spot_price = 0.0001