diff --git a/.docs/craig-code-engine.md b/.docs/craig-code-engine.md
index 84b81a9d..31c76092 100644
--- a/.docs/craig-code-engine.md
+++ b/.docs/craig-code-engine.md
@@ -44,7 +44,13 @@ If you do not want Power VS workspaces created in every zone, you can create the
#### generate-env.sh prerequisites
- [jq](https://jqlang.github.io/jq/) v1.7 or higher
- ibmcloud CLI
-- Bash version 4 or higher
+
+#### Downloading generate-env.sh in IBM Cloud Shell
+From within IBM Cloud Shell run the following two commands to download the script and make it executable:
+```bash
+wget https://raw.githubusercontent.com/IBM/CRAIG/main/generate-env.sh
+chmod 755 generate-env.sh
+```
To generate an env containing all of the workspaces in your account, you can run the following command:
diff --git a/.docs/poc-template-worksheet.md b/.docs/poc-template-worksheet.md
new file mode 100644
index 00000000..a618074e
--- /dev/null
+++ b/.docs/poc-template-worksheet.md
@@ -0,0 +1,33 @@
+# Worksheet for Power VS POC template
+
+Set up access policies in your IBM Cloud account following the instructions [here](access-policies.md)
+
+Create in your IBM cloud account:
+- API key
+
+## Record the values for the keys mentioned below:
+
+### On-Premises information
+
+| *Keys* | *Values* |
+| ------------------------------------------------------------------------------------------- | -------- |
+| [*On-Prem network CIDRs](powervs-poc.md#on-premises-network-cidrs-and-peer-address) | |
+| [*On-Prem Peer Address](powervs-poc.md#on-premises-network-cidrs-and-peer-address) | |
+| [*On-Prem connection preshared key](powervs-poc.md#configuring-the-on-premises-vpn-gateway) | |
+
+### IBM Cloud information
+
+| *Keys* | *Values* |
+| -------------------------------------------------------------------------------------------------------------- | -------- |
+| [Region](powervs-poc.md#region-and-power-vs-zone) | |
+| [Power VS Zone](powervs-poc.md#region-and-power-vs-zone) | |
+| [resource name prefix](powervs-poc.md#resource-prefix) (optional) | |
+| [*Public SSH key](powervs-poc.md#set-public-ssh-keys) | |
+| [*IBM Cloud API Key](https://cloud.ibm.com/docs/account?topic=account-userapikey&interface=ui#create_user_key) | |
+| [VPC VPN network CIDR](powervs-poc.md#vpc-network-cidr) | |
+| [VPC VSI network CIDR](powervs-poc.md#vpc-network-cidr) | |
+| [VPC VPE network CIDR](powervs-poc.md#vpc-network-cidr) | |
+| [Power VS network CIDR](powervs-poc.md#power-virtual-server-network-cidr) | |
+
+
+*Note*: The asterisk (*) denotes mandatory fields; optional values can be provided, otherwise CRAIG will default to predefined values.
\ No newline at end of file
diff --git a/.docs/power-vs-workspace-deployment.md b/.docs/power-vs-workspace-deployment.md
index fa6e4a80..0b35e781 100644
--- a/.docs/power-vs-workspace-deployment.md
+++ b/.docs/power-vs-workspace-deployment.md
@@ -31,7 +31,6 @@ If you do not want Power VS workspaces created in every zone or if you want to u
#### generate-env.sh prerequisites
- [jq](https://jqlang.github.io/jq/) v1.7 or higher
- [IBM Cloud CLI](https://cloud.ibm.com/docs/cli?topic=cli-getting-started)
-- Bash version 4 or higher
To generate a `.env` file containing all of the workspaces in your account, you can run the following command:
diff --git a/.docs/powervs-poc.md b/.docs/powervs-poc.md
index c0297c9b..18dbcfda 100644
--- a/.docs/powervs-poc.md
+++ b/.docs/powervs-poc.md
@@ -77,7 +77,7 @@ After changing the region and zone(s) in the options, the existing Power VS work
The default AIX and IBM i virtual servers in the template will also need to be updated with the newly selected images. Click on each of the red Virtual Server icons, set their image, and click the save button for the VSI.
### VPC network CIDR
-To change CIDR of the VPC networks, click on `VPC Networks` on the left navigation bar. Click on the the network you would like to update, change the "Advanced Configuration" to true, de-select zones 2 and 3 from the Zones seletor and press the Save button.
+To change CIDR of the VPC networks, click on `VPC Networks` on the left navigation bar. Click on the the network you would like to update, change the "Advanced Configuration" to true, de-select zones 2 and 3 from the Zones selector and press the Save button.
Finally, change the subnet CIDR and click the Save button next to the subnet name.
diff --git a/.docs/schematics-how-to.md b/.docs/schematics-how-to.md
index 8df0217a..7b28515e 100644
--- a/.docs/schematics-how-to.md
+++ b/.docs/schematics-how-to.md
@@ -8,8 +8,8 @@ The `API_KEY` variable must be set in the `.env`. If CRAIG is deployed in IBM Co
### Access Policy
In order to allow Schematics integration, users should make sure they have the following access policy roles for the Schematics service:
->* `Editor` or greater Platform access
>* `Writer` or greater Service access
+>* `Editor` or greater Platform access
These roles allow the integration with Schematics including the Schematics workspace creation and the upload of the project. However, to create and manage the IBM Cloud resources in the template, you must be assigned the IAM platform or service access role for the individual IBM Cloud resources that are in the template. See the IBM Cloud documentation for the various services for specific roles required.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a47d8bc7..fd7a8f6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,33 @@
All notable changes to this project will be documented in this file.
+## 1.13.0
+
+### Upgrade Notes
+
+- A new, improved version of the CRAIG tutorial is available in the README. This tutorial shows users how to install CRAIG, use the new V2 GUI, and integrate deployments with schematics
+- Deprecated field for VSI primary IPs has been change to a version that no longer throws Terraform warnings
+
+### Features
+
+- Users will now get feedback when a Power VS Workspace has no VTL images when trying to create or update a VTL instance. Images can be added from the Power VS Workspace form
+- Icons for deployments within imported subnets are now rendered within that subnet on the `/v2/vpcDeployments` page
+- Users can now create Classic VSI from the form page `/form/classicVsi`
+- Power VS Workspace names, ids, and CRNs are now included as outputs in the `outputs.tf` file of any CRAIG Terraform template
+- Power VS High Availability is now supported for `mad02`, `mad04`, `us-east`, `wdc06`, `us-south`, `eu-de-1`, and `eu-de-2`
+- When updating a Power VS Instance name, references to that instance are now updated to match the new name
+- Users can now upload JSON directly to CRAIG from the local file explorer from the Projects page by clicking the new `Upload JSON` button
+- Users can now create Classic Bare Metal Servers from the form page `/form/classicBareMetal`
+- Power VS Instance Primary IP addresses are now included as outputs in the `outputs.tf` file of any CRAIG Terraform template
+- When downloading a `.zip` file of an environment from the CRAIG GUI, an image of the current environment is now included in the archive
+- CRAIG now supports adding a Pin Policy to Power VS instances
+
+### Fixes
+
+- Fixed an issue with button hoverText alignment causing overflow in forms
+- Fixed an issue causing an incorrect name for DNS Services in the `/v2/services` page
+- Fixed an issue preventing users from inputing decimal values for CPU when creating a FalconStor VTL instance with a shared processor type
+
## 1.12.2
### Upgrade Notes
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..de488d01
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,128 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+email.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..f57fd948
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,28 @@
+# Contributing
+
+Found a bug or need an additional feature? File an issue in this repository with the following information and they will be responded to in a timely manner.
+
+## Bugs
+
+- A detailed title describing the issue with the current release and the tag `[BUG]`. For sprint one, filing a bug would have the title `[0.1.0][BUG] `
+- Steps to recreate said bug (including non-sensitive variables)
+- (optional) Corresponding output logs **as text or as part of a code block**
+- Tag bug issues with the `bug` label
+- If you come across a vulnerability that needs to be addressed immediately, use the `vulnerability` label
+
+
+## Features
+
+- A detailed title describing the desired feature that includes the current release. For sprint one, a feature would have the title `[0.1.0] `
+- A detailed description including the user story
+- A checkbox list of needed features
+- Tag the issue with the `enhancement` label
+
+Want to work on an issue? Be sure to assign it to yourself and branch from main. When you're done making the required changes, create a pull request.
+
+## Pull requests
+
+**Do not merge directly to main**. Pull requests should reference the corresponding issue filed in this repository. Please be sure to maintain **code coverage** before merging.
+
+At least **two** reviews are required to merge a pull request. When creating a pull request, please ensure that details about unexpected changes to the codebase are provided in the description.
+
diff --git a/README.md b/README.md
index 2a51f2ef..ba11be9e 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,16 @@ CRAIG configures infrastructure using JSON to create full VPC networks, manage s
---
+### Tutorial Video
+
+[Follow this tutorial](https://ibm.box.com/v/craigTutorialVideo) for step-by-step instructions on how to get started with CRAIG.
+
+***Ensure `Quality: 1080p` is selected within Box video player settings for the best viewing experience.***
+
+***Last Updated: March 11th, 2024***
+
+---
+
## Installation
1. [Running CRAIG Application Locally](#running-craig-application-locally)
@@ -27,14 +37,6 @@ CRAIG configures infrastructure using JSON to create full VPC networks, manage s
---
-### Tutorial Video
-
-[Follow this tutorial](https://ibm.box.com/v/craigTutorialVideo) for step-by-step instructions on how to get started with CRAIG.
-
-***Ensure `Quality: 1080p` is selected within Box video player settings for the best viewing experience.***
-
----
-
### Running CRAIG Application Locally
To get started using CRAIG locally, follow these steps:
@@ -74,12 +76,12 @@ Within the root directory is a script `deploy.sh` which deploys CRAIG to IBM Clo
Users should make sure they have the following access policy roles for the IBM Code Engine service set within their IBM Cloud Account:
-- IBM Cloud Platform Roles: Editor or Higher
-- Code Engine Service Roles: Writer or Higher
+>* `Writer` or greater Service access
+>* `Editor` or greater Platform access
Users should also make sure they have the following access policy roles for the IBM Cloud Container Registry service set within their IBM Cloud Account:
-- Container Registry Service: Manager
+>* `Manager` Service access
These permissions are the minimum requirements needed in order to provision a Code Engine project, Container Registry namespace, application, image build, and secrets using the `deploy.sh` script.
@@ -108,7 +110,14 @@ chmod 755 deploy.sh
By default the script will securely prompt you for your API key. It may also be read from an environment variable or specified as a command line argument. See the `deploy.sh -h` usage for more information.
- If CRAIG is used for Power VS configuration, Power VS workspaces must exist in the zones that CRAIG projects will use. The deploy script can create the Power Virtual Server workspaces in every Power VS zone worldwide and automatically integrate them with the CRAIG deployment. The deploy script uses a Schematics workspace and Terraform to drive the creation and deletion of the Power Virtual Server workspaces. Specify the `-z` parameter to automatically create the Power Virtual Server workspaces:
+ If CRAIG is used for Power VS configuration, Power Virtual Server workspaces must exist in the zones that CRAIG projects will use. The deploy script can create the Power Virtual Server workspaces in every Power VS zone worldwide and automatically integrate them with the CRAIG deployment.
+
+ The deploy script uses a Schematics workspace and Terraform to drive the creation and deletion of the Power Virtual Server workspaces. In order to allow Schematics integration, users should make sure they have the following access policy roles for the Schematics service set within their IBM Cloud Account:
+
+>* `Writer` or greater Service access
+>* `Editor` or greater Platform access
+
+ Once access policy roles for the Schematics service are properly configured, users can specify the `-z` parameter to automatically create the Power Virtual Server workspaces alongside your CRAIG deployment:
```bash
./deploy.sh -z
@@ -161,9 +170,11 @@ Make sure to set the `API_KEY` variable in a `.env` file to be used for IBM Clou
See `.env.example` found [here](./.env.example)
```shell
-docker run -it --env-file .env -- craig
+docker run -t -d -p 8080:8080 --env-file .env -- craig
```
+CRAIG is now available at http://localhost:8080.
+
---
### Setting Up CRAIG Development Environment
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..d498ccee
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,12 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported |
+| ---------- | ------------------- |
+| > 1.10.x | :white_check_mark: |
+| < 1.10.0 | :x: |
+
+## Reporting a Vulnerability
+
+- Create a GitHub issue and use the `vulnerability` label
\ No newline at end of file
diff --git a/ansible/template-test/main.yml b/ansible/template-test/main.yml
index 18405485..912272c9 100644
--- a/ansible/template-test/main.yml
+++ b/ansible/template-test/main.yml
@@ -10,25 +10,9 @@
- name: Upload CRAIG template to schematics workspace
hosts: localhost
vars_files: ./vars/vars.yml
-- name: Get IAM token
- hosts: localhost
- vars_files: ./vars/vars.yml # variables declared in variables file are added to role automatically
roles:
- role: get_iam_token
-- name: "Download Template Tarball"
- hosts: localhost
- vars_files: ./vars/vars.yml
- tasks:
- - name: Download {{template}}.tar to current directory
- get_url:
- url: "{{craig_url}}/{{template}}"
- dest: "{{playbook_dir}}/{{template}}.tar"
- async: 120
- retries: 10
-- name: Create Schematics Workspace
- hosts: localhost
- vars_files: ./vars/vars.yml # variables declared in variables file are added to role automatically
- roles:
+ - role: download_tar
- role: create_schematics_workspace
vars:
description: Automated CRAIG Testing Workspace
@@ -69,70 +53,36 @@
body_format: json
body:
variablestore: "{{ variablestore }}"
- - name: Start generate plan action
- uri:
- url: https://schematics.cloud.ibm.com/v1/workspaces/{{ workspace.json.id }}/plan
- method: POST
- body_format: json
- headers:
- Authorization: Bearer {{token.json.access_token}}
- status_code: 202
- register: job
- - name: Ensure generate plan finishes
- uri:
- url: https://schematics.cloud.ibm.com/v2/jobs/{{job.json.activityid}}
- method: GET
- body_format: json
- headers:
- Authorization: Bearer {{token.json.access_token}}
- register: plan
- until: plan.json.status.workspace_job_status.status_code == "job_finished" or plan.json.status.workspace_job_status.status_code == "job_failed"
- failed_when: plan.json.status.workspace_job_status.status_code == "job_failed"
- delay: 90
- retries: 50
- - name: Start apply plan action
- uri:
- url: https://schematics.cloud.ibm.com/v1/workspaces/{{workspace.json.id}}/apply
- method: PUT
- body_format: json
- headers:
- Authorization: Bearer {{token.json.access_token}}
- status_code: 202
- register: apply
- - name: Ensure apply plan finishes
- uri:
- url: https://schematics.cloud.ibm.com/v2/jobs/{{apply.json.activityid}}
- method: GET
- body_format: json
- headers:
- Authorization: Bearer {{token.json.access_token}}
- register: apply_plan
- until: apply_plan.json.status.workspace_job_status.status_code == "job_finished" or apply_plan.json.status.workspace_job_status.status_code == "job_failed"
- failed_when: apply_plan.json.status.workspace_job_status.status_code == "job_failed"
- delay: 120
- retries: 50
- - name: Start destroy action
- uri:
- url: https://schematics.cloud.ibm.com/v1/workspaces/{{workspace.json.id}}/destroy
- method: PUT
- body_format: json
- headers:
- Authorization: Bearer {{token.json.access_token}}
- status_code: 202
- register: destroy
- - name: Ensure destory finishes
- uri:
- url: https://schematics.cloud.ibm.com/v2/jobs/{{destroy.json.activityid}}
- method: GET
- body_format: json
- headers:
- Authorization: Bearer {{token.json.access_token}}
- register: destroy_plan
- until: destroy_plan.json.status.workspace_job_status.status_code == "job_finished" or destroy_plan.json.status.workspace_job_status.status_code == "job_failed"
- failed_when: destroy_plan.json.status.workspace_job_status.status_code == "job_failed"
- delay: 120
- retries: 50
- - name: Delete local {{template}}.tar file
- file:
- state: absent
- path: /{{playbook_dir}}/{{template}}.tar
\ No newline at end of file
+- name: Start generate plan action
+ hosts: localhost
+ vars_files: ./vars/vars.yml
+ roles:
+ - role: get_iam_token
+ - role: action
+ vars:
+ action: plan
+ - role: get_iam_token
+ - role: await_action
+ vars:
+ action: plan
+ register_name: await_plan
+ - role: get_iam_token
+ - role: action
+ vars:
+ action: apply
+ - role: get_iam_token
+ - role: await_action
+ vars:
+ action: apply
+ register_name: await_apply
+ - role: get_iam_token
+ - role: action
+ vars:
+ action: destroy
+ - role: get_iam_token
+ - role: await_action
+ vars:
+ action: destroy
+ register_name: await_destroy
+ - role: get_iam_token
+ - role: delete_schematics_workspace
\ No newline at end of file
diff --git a/ansible/template-test/roles/action/README.md b/ansible/template-test/roles/action/README.md
new file mode 100644
index 00000000..225dd44b
--- /dev/null
+++ b/ansible/template-test/roles/action/README.md
@@ -0,0 +1,38 @@
+Role Name
+=========
+
+A brief description of the role goes here.
+
+Requirements
+------------
+
+Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
+
+Role Variables
+--------------
+
+A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
+
+Dependencies
+------------
+
+A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
+
+Example Playbook
+----------------
+
+Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
+
+ - hosts: servers
+ roles:
+ - { role: username.rolename, x: 42 }
+
+License
+-------
+
+BSD
+
+Author Information
+------------------
+
+An optional section for the role authors to include contact information, or a website (HTML is not allowed).
diff --git a/ansible/template-test/roles/action/defaults/main.yml b/ansible/template-test/roles/action/defaults/main.yml
new file mode 100644
index 00000000..0686718f
--- /dev/null
+++ b/ansible/template-test/roles/action/defaults/main.yml
@@ -0,0 +1,2 @@
+---
+# defaults file for action
diff --git a/ansible/template-test/roles/action/handlers/main.yml b/ansible/template-test/roles/action/handlers/main.yml
new file mode 100644
index 00000000..830712d2
--- /dev/null
+++ b/ansible/template-test/roles/action/handlers/main.yml
@@ -0,0 +1,2 @@
+---
+# handlers file for action
diff --git a/ansible/template-test/roles/action/meta/main.yml b/ansible/template-test/roles/action/meta/main.yml
new file mode 100644
index 00000000..c572acc9
--- /dev/null
+++ b/ansible/template-test/roles/action/meta/main.yml
@@ -0,0 +1,52 @@
+galaxy_info:
+ author: your name
+ description: your role description
+ company: your company (optional)
+
+ # If the issue tracker for your role is not on github, uncomment the
+ # next line and provide a value
+ # issue_tracker_url: http://example.com/issue/tracker
+
+ # Choose a valid license ID from https://spdx.org - some suggested licenses:
+ # - BSD-3-Clause (default)
+ # - MIT
+ # - GPL-2.0-or-later
+ # - GPL-3.0-only
+ # - Apache-2.0
+ # - CC-BY-4.0
+ license: license (GPL-2.0-or-later, MIT, etc)
+
+ min_ansible_version: 2.1
+
+ # If this a Container Enabled role, provide the minimum Ansible Container version.
+ # min_ansible_container_version:
+
+ #
+ # Provide a list of supported platforms, and for each platform a list of versions.
+ # If you don't wish to enumerate all versions for a particular platform, use 'all'.
+ # To view available platforms and versions (or releases), visit:
+ # https://galaxy.ansible.com/api/v1/platforms/
+ #
+ # platforms:
+ # - name: Fedora
+ # versions:
+ # - all
+ # - 25
+ # - name: SomePlatform
+ # versions:
+ # - all
+ # - 1.0
+ # - 7
+ # - 99.99
+
+ galaxy_tags: []
+ # List tags for your role here, one per line. A tag is a keyword that describes
+ # and categorizes the role. Users find roles by searching for tags. Be sure to
+ # remove the '[]' above, if you add tags to this list.
+ #
+ # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
+ # Maximum 20 tags per role.
+
+dependencies: []
+ # List your role dependencies here, one per line. Be sure to remove the '[]' above,
+ # if you add dependencies to this list.
diff --git a/ansible/template-test/roles/action/tasks/main.yml b/ansible/template-test/roles/action/tasks/main.yml
new file mode 100644
index 00000000..9ad4bec3
--- /dev/null
+++ b/ansible/template-test/roles/action/tasks/main.yml
@@ -0,0 +1,11 @@
+---
+# tasks file for action
+- name: Start {{ action }} action
+ uri:
+ url: https://schematics.cloud.ibm.com/v1/workspaces/{{ workspace.json.id }}/{{ action }}
+ method: "{{ 'POST' if action == 'plan' else 'PUT' }}"
+ body_format: json
+ headers:
+ Authorization: Bearer {{token.json.access_token}}
+ status_code: 202
+ register: action_result
\ No newline at end of file
diff --git a/ansible/template-test/roles/action/tests/inventory b/ansible/template-test/roles/action/tests/inventory
new file mode 100644
index 00000000..878877b0
--- /dev/null
+++ b/ansible/template-test/roles/action/tests/inventory
@@ -0,0 +1,2 @@
+localhost
+
diff --git a/ansible/template-test/roles/action/tests/test.yml b/ansible/template-test/roles/action/tests/test.yml
new file mode 100644
index 00000000..a6919dbf
--- /dev/null
+++ b/ansible/template-test/roles/action/tests/test.yml
@@ -0,0 +1,5 @@
+---
+- hosts: localhost
+ remote_user: root
+ roles:
+ - action
diff --git a/ansible/template-test/roles/action/vars/main.yml b/ansible/template-test/roles/action/vars/main.yml
new file mode 100644
index 00000000..5f086714
--- /dev/null
+++ b/ansible/template-test/roles/action/vars/main.yml
@@ -0,0 +1,2 @@
+---
+# vars file for action
diff --git a/ansible/template-test/roles/await_action/README.md b/ansible/template-test/roles/await_action/README.md
new file mode 100644
index 00000000..225dd44b
--- /dev/null
+++ b/ansible/template-test/roles/await_action/README.md
@@ -0,0 +1,38 @@
+Role Name
+=========
+
+A brief description of the role goes here.
+
+Requirements
+------------
+
+Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
+
+Role Variables
+--------------
+
+A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
+
+Dependencies
+------------
+
+A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
+
+Example Playbook
+----------------
+
+Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
+
+ - hosts: servers
+ roles:
+ - { role: username.rolename, x: 42 }
+
+License
+-------
+
+BSD
+
+Author Information
+------------------
+
+An optional section for the role authors to include contact information, or a website (HTML is not allowed).
diff --git a/ansible/template-test/roles/await_action/defaults/main.yml b/ansible/template-test/roles/await_action/defaults/main.yml
new file mode 100644
index 00000000..40b387ed
--- /dev/null
+++ b/ansible/template-test/roles/await_action/defaults/main.yml
@@ -0,0 +1,2 @@
+---
+# defaults file for await_action
diff --git a/ansible/template-test/roles/await_action/handlers/main.yml b/ansible/template-test/roles/await_action/handlers/main.yml
new file mode 100644
index 00000000..a886d9d5
--- /dev/null
+++ b/ansible/template-test/roles/await_action/handlers/main.yml
@@ -0,0 +1,2 @@
+---
+# handlers file for await_action
diff --git a/ansible/template-test/roles/await_action/meta/main.yml b/ansible/template-test/roles/await_action/meta/main.yml
new file mode 100644
index 00000000..c572acc9
--- /dev/null
+++ b/ansible/template-test/roles/await_action/meta/main.yml
@@ -0,0 +1,52 @@
+galaxy_info:
+ author: your name
+ description: your role description
+ company: your company (optional)
+
+ # If the issue tracker for your role is not on github, uncomment the
+ # next line and provide a value
+ # issue_tracker_url: http://example.com/issue/tracker
+
+ # Choose a valid license ID from https://spdx.org - some suggested licenses:
+ # - BSD-3-Clause (default)
+ # - MIT
+ # - GPL-2.0-or-later
+ # - GPL-3.0-only
+ # - Apache-2.0
+ # - CC-BY-4.0
+ license: license (GPL-2.0-or-later, MIT, etc)
+
+ min_ansible_version: 2.1
+
+ # If this a Container Enabled role, provide the minimum Ansible Container version.
+ # min_ansible_container_version:
+
+ #
+ # Provide a list of supported platforms, and for each platform a list of versions.
+ # If you don't wish to enumerate all versions for a particular platform, use 'all'.
+ # To view available platforms and versions (or releases), visit:
+ # https://galaxy.ansible.com/api/v1/platforms/
+ #
+ # platforms:
+ # - name: Fedora
+ # versions:
+ # - all
+ # - 25
+ # - name: SomePlatform
+ # versions:
+ # - all
+ # - 1.0
+ # - 7
+ # - 99.99
+
+ galaxy_tags: []
+ # List tags for your role here, one per line. A tag is a keyword that describes
+ # and categorizes the role. Users find roles by searching for tags. Be sure to
+ # remove the '[]' above, if you add tags to this list.
+ #
+ # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
+ # Maximum 20 tags per role.
+
+dependencies: []
+ # List your role dependencies here, one per line. Be sure to remove the '[]' above,
+ # if you add dependencies to this list.
diff --git a/ansible/template-test/roles/await_action/tasks/main.yml b/ansible/template-test/roles/await_action/tasks/main.yml
new file mode 100644
index 00000000..969284ae
--- /dev/null
+++ b/ansible/template-test/roles/await_action/tasks/main.yml
@@ -0,0 +1,15 @@
+- name: Ensure {{ action }} finishes
+ uri:
+ url: https://schematics.cloud.ibm.com/v2/jobs/{{ action_result.json.activityid }}
+ method: GET
+ body_format: json
+ headers:
+ Authorization: Bearer {{ token.json.access_token }}
+ register: dynamic_value
+ until: dynamic_value.json.status.workspace_job_status.status_code == "job_finished" or dynamic_value.json.status.workspace_job_status.status_code == "job_failed"
+ failed_when: dynamic_value.json.status.workspace_job_status.status_code == "job_failed"
+ delay: 120
+ retries: 50
+- name: Set Variable
+ set_fact:
+ "{{ register_name }}": "{{ dynamic_value }}"
\ No newline at end of file
diff --git a/ansible/template-test/roles/await_action/tests/inventory b/ansible/template-test/roles/await_action/tests/inventory
new file mode 100644
index 00000000..878877b0
--- /dev/null
+++ b/ansible/template-test/roles/await_action/tests/inventory
@@ -0,0 +1,2 @@
+localhost
+
diff --git a/ansible/template-test/roles/await_action/tests/test.yml b/ansible/template-test/roles/await_action/tests/test.yml
new file mode 100644
index 00000000..83cf53f8
--- /dev/null
+++ b/ansible/template-test/roles/await_action/tests/test.yml
@@ -0,0 +1,5 @@
+---
+- hosts: localhost
+ remote_user: root
+ roles:
+ - await_action
diff --git a/ansible/template-test/roles/await_action/vars/main.yml b/ansible/template-test/roles/await_action/vars/main.yml
new file mode 100644
index 00000000..72bd7e8c
--- /dev/null
+++ b/ansible/template-test/roles/await_action/vars/main.yml
@@ -0,0 +1,2 @@
+---
+# vars file for await_action
diff --git a/ansible/template-test/roles/delete_schematics_workspace/README.md b/ansible/template-test/roles/delete_schematics_workspace/README.md
new file mode 100644
index 00000000..225dd44b
--- /dev/null
+++ b/ansible/template-test/roles/delete_schematics_workspace/README.md
@@ -0,0 +1,38 @@
+Role Name
+=========
+
+A brief description of the role goes here.
+
+Requirements
+------------
+
+Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
+
+Role Variables
+--------------
+
+A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
+
+Dependencies
+------------
+
+A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
+
+Example Playbook
+----------------
+
+Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
+
+ - hosts: servers
+ roles:
+ - { role: username.rolename, x: 42 }
+
+License
+-------
+
+BSD
+
+Author Information
+------------------
+
+An optional section for the role authors to include contact information, or a website (HTML is not allowed).
diff --git a/ansible/template-test/roles/delete_schematics_workspace/defaults/main.yml b/ansible/template-test/roles/delete_schematics_workspace/defaults/main.yml
new file mode 100644
index 00000000..87646b0e
--- /dev/null
+++ b/ansible/template-test/roles/delete_schematics_workspace/defaults/main.yml
@@ -0,0 +1,2 @@
+---
+# defaults file for delete_schematics_workspace
diff --git a/ansible/template-test/roles/delete_schematics_workspace/handlers/main.yml b/ansible/template-test/roles/delete_schematics_workspace/handlers/main.yml
new file mode 100644
index 00000000..3b193b92
--- /dev/null
+++ b/ansible/template-test/roles/delete_schematics_workspace/handlers/main.yml
@@ -0,0 +1,2 @@
+---
+# handlers file for delete_schematics_workspace
diff --git a/ansible/template-test/roles/delete_schematics_workspace/meta/main.yml b/ansible/template-test/roles/delete_schematics_workspace/meta/main.yml
new file mode 100644
index 00000000..c572acc9
--- /dev/null
+++ b/ansible/template-test/roles/delete_schematics_workspace/meta/main.yml
@@ -0,0 +1,52 @@
+galaxy_info:
+ author: your name
+ description: your role description
+ company: your company (optional)
+
+ # If the issue tracker for your role is not on github, uncomment the
+ # next line and provide a value
+ # issue_tracker_url: http://example.com/issue/tracker
+
+ # Choose a valid license ID from https://spdx.org - some suggested licenses:
+ # - BSD-3-Clause (default)
+ # - MIT
+ # - GPL-2.0-or-later
+ # - GPL-3.0-only
+ # - Apache-2.0
+ # - CC-BY-4.0
+ license: license (GPL-2.0-or-later, MIT, etc)
+
+ min_ansible_version: 2.1
+
+ # If this a Container Enabled role, provide the minimum Ansible Container version.
+ # min_ansible_container_version:
+
+ #
+ # Provide a list of supported platforms, and for each platform a list of versions.
+ # If you don't wish to enumerate all versions for a particular platform, use 'all'.
+ # To view available platforms and versions (or releases), visit:
+ # https://galaxy.ansible.com/api/v1/platforms/
+ #
+ # platforms:
+ # - name: Fedora
+ # versions:
+ # - all
+ # - 25
+ # - name: SomePlatform
+ # versions:
+ # - all
+ # - 1.0
+ # - 7
+ # - 99.99
+
+ galaxy_tags: []
+ # List tags for your role here, one per line. A tag is a keyword that describes
+ # and categorizes the role. Users find roles by searching for tags. Be sure to
+ # remove the '[]' above, if you add tags to this list.
+ #
+ # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
+ # Maximum 20 tags per role.
+
+dependencies: []
+ # List your role dependencies here, one per line. Be sure to remove the '[]' above,
+ # if you add dependencies to this list.
diff --git a/ansible/template-test/roles/delete_schematics_workspace/tasks/main.yml b/ansible/template-test/roles/delete_schematics_workspace/tasks/main.yml
new file mode 100644
index 00000000..9a02962b
--- /dev/null
+++ b/ansible/template-test/roles/delete_schematics_workspace/tasks/main.yml
@@ -0,0 +1,13 @@
+---
+# tasks file for delete_schematics_workspace
+- name: Delete Schematics Workspace
+ uri:
+ url: https://schematics.cloud.ibm.com/v1/workspaces/{{workspace.json.id}}
+ method: DELETE
+ headers:
+ Authorization: Bearer {{token.json.access_token}}
+ status_code: 200
+ register: delete_response
+ until: delete_response.status == 200
+ retries: 2
+ delay: 120
\ No newline at end of file
diff --git a/ansible/template-test/roles/delete_schematics_workspace/tests/inventory b/ansible/template-test/roles/delete_schematics_workspace/tests/inventory
new file mode 100644
index 00000000..878877b0
--- /dev/null
+++ b/ansible/template-test/roles/delete_schematics_workspace/tests/inventory
@@ -0,0 +1,2 @@
+localhost
+
diff --git a/ansible/template-test/roles/delete_schematics_workspace/tests/test.yml b/ansible/template-test/roles/delete_schematics_workspace/tests/test.yml
new file mode 100644
index 00000000..3c1a8e6a
--- /dev/null
+++ b/ansible/template-test/roles/delete_schematics_workspace/tests/test.yml
@@ -0,0 +1,5 @@
+---
+- hosts: localhost
+ remote_user: root
+ roles:
+ - delete_schematics_workspace
diff --git a/ansible/template-test/roles/delete_schematics_workspace/vars/main.yml b/ansible/template-test/roles/delete_schematics_workspace/vars/main.yml
new file mode 100644
index 00000000..7ae746a9
--- /dev/null
+++ b/ansible/template-test/roles/delete_schematics_workspace/vars/main.yml
@@ -0,0 +1,2 @@
+---
+# vars file for delete_schematics_workspace
diff --git a/ansible/template-test/roles/download_tar/README.md b/ansible/template-test/roles/download_tar/README.md
new file mode 100644
index 00000000..225dd44b
--- /dev/null
+++ b/ansible/template-test/roles/download_tar/README.md
@@ -0,0 +1,38 @@
+Role Name
+=========
+
+A brief description of the role goes here.
+
+Requirements
+------------
+
+Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
+
+Role Variables
+--------------
+
+A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
+
+Dependencies
+------------
+
+A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
+
+Example Playbook
+----------------
+
+Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
+
+ - hosts: servers
+ roles:
+ - { role: username.rolename, x: 42 }
+
+License
+-------
+
+BSD
+
+Author Information
+------------------
+
+An optional section for the role authors to include contact information, or a website (HTML is not allowed).
diff --git a/ansible/template-test/roles/download_tar/defaults/main.yml b/ansible/template-test/roles/download_tar/defaults/main.yml
new file mode 100644
index 00000000..e2612f9b
--- /dev/null
+++ b/ansible/template-test/roles/download_tar/defaults/main.yml
@@ -0,0 +1,2 @@
+---
+# defaults file for download_tar
diff --git a/ansible/template-test/roles/download_tar/handlers/main.yml b/ansible/template-test/roles/download_tar/handlers/main.yml
new file mode 100644
index 00000000..8954f993
--- /dev/null
+++ b/ansible/template-test/roles/download_tar/handlers/main.yml
@@ -0,0 +1,2 @@
+---
+# handlers file for download_tar
diff --git a/ansible/template-test/roles/download_tar/meta/main.yml b/ansible/template-test/roles/download_tar/meta/main.yml
new file mode 100644
index 00000000..c572acc9
--- /dev/null
+++ b/ansible/template-test/roles/download_tar/meta/main.yml
@@ -0,0 +1,52 @@
+galaxy_info:
+ author: your name
+ description: your role description
+ company: your company (optional)
+
+ # If the issue tracker for your role is not on github, uncomment the
+ # next line and provide a value
+ # issue_tracker_url: http://example.com/issue/tracker
+
+ # Choose a valid license ID from https://spdx.org - some suggested licenses:
+ # - BSD-3-Clause (default)
+ # - MIT
+ # - GPL-2.0-or-later
+ # - GPL-3.0-only
+ # - Apache-2.0
+ # - CC-BY-4.0
+ license: license (GPL-2.0-or-later, MIT, etc)
+
+ min_ansible_version: 2.1
+
+ # If this a Container Enabled role, provide the minimum Ansible Container version.
+ # min_ansible_container_version:
+
+ #
+ # Provide a list of supported platforms, and for each platform a list of versions.
+ # If you don't wish to enumerate all versions for a particular platform, use 'all'.
+ # To view available platforms and versions (or releases), visit:
+ # https://galaxy.ansible.com/api/v1/platforms/
+ #
+ # platforms:
+ # - name: Fedora
+ # versions:
+ # - all
+ # - 25
+ # - name: SomePlatform
+ # versions:
+ # - all
+ # - 1.0
+ # - 7
+ # - 99.99
+
+ galaxy_tags: []
+ # List tags for your role here, one per line. A tag is a keyword that describes
+ # and categorizes the role. Users find roles by searching for tags. Be sure to
+ # remove the '[]' above, if you add tags to this list.
+ #
+ # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
+ # Maximum 20 tags per role.
+
+dependencies: []
+ # List your role dependencies here, one per line. Be sure to remove the '[]' above,
+ # if you add dependencies to this list.
diff --git a/ansible/template-test/roles/download_tar/tasks/main.yml b/ansible/template-test/roles/download_tar/tasks/main.yml
new file mode 100644
index 00000000..35686117
--- /dev/null
+++ b/ansible/template-test/roles/download_tar/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+# tasks file for download_tar
+- name: Download {{template}}.tar to current directory
+ get_url:
+ url: "{{craig_url}}/{{template}}"
+ dest: "{{playbook_dir}}/{{template}}.tar"
+ async: 120
+ retries: 10
\ No newline at end of file
diff --git a/ansible/template-test/roles/download_tar/tests/inventory b/ansible/template-test/roles/download_tar/tests/inventory
new file mode 100644
index 00000000..878877b0
--- /dev/null
+++ b/ansible/template-test/roles/download_tar/tests/inventory
@@ -0,0 +1,2 @@
+localhost
+
diff --git a/ansible/template-test/roles/download_tar/tests/test.yml b/ansible/template-test/roles/download_tar/tests/test.yml
new file mode 100644
index 00000000..a45b7871
--- /dev/null
+++ b/ansible/template-test/roles/download_tar/tests/test.yml
@@ -0,0 +1,5 @@
+---
+- hosts: localhost
+ remote_user: root
+ roles:
+ - download_tar
diff --git a/ansible/template-test/roles/download_tar/vars/main.yml b/ansible/template-test/roles/download_tar/vars/main.yml
new file mode 100644
index 00000000..c9373e95
--- /dev/null
+++ b/ansible/template-test/roles/download_tar/vars/main.yml
@@ -0,0 +1,2 @@
+---
+# vars file for download_tar
diff --git a/client/package-lock.json b/client/package-lock.json
index 1c1f469f..69abd4ee 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "craig",
- "version": "1.12.1",
+ "version": "1.13.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "craig",
- "version": "1.12.1",
+ "version": "1.13.0",
"license": "Apache-2.0",
"dependencies": {
"@apollo/client": "^3.4.10",
@@ -17,6 +17,7 @@
"chart.js": "^4.4.1",
"file-saver": "^2.0.5",
"graphql": "15.5.2",
+ "html-to-image": "^1.11.11",
"json-to-tf": "^0.3.1",
"jszip": "^3.10.1",
"lazy-z": "^1.11.17",
@@ -13378,6 +13379,11 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
+ "node_modules/html-to-image": {
+ "version": "1.11.11",
+ "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz",
+ "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA=="
+ },
"node_modules/htmlparser2": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
diff --git a/client/package.json b/client/package.json
index 6c879880..f0a5d410 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
{
"name": "craig",
- "version": "1.12.1",
+ "version": "1.13.0",
"private": true,
"license": "Apache-2.0",
"scripts": {
@@ -23,6 +23,7 @@
"chart.js": "^4.4.1",
"file-saver": "^2.0.5",
"graphql": "15.5.2",
+ "html-to-image": "^1.11.11",
"json-to-tf": "^0.3.1",
"jszip": "^3.10.1",
"lazy-z": "^1.11.17",
diff --git a/client/src/Craig.js b/client/src/Craig.js
index b77337bf..49d15869 100644
--- a/client/src/Craig.js
+++ b/client/src/Craig.js
@@ -45,6 +45,8 @@ import { Overview } from "./components/pages/diagrams/Overview.js";
import PowerDiagram from "./components/pages/power/Power.js";
import VpcConnectivityPage from "./components/pages/vpc/Connectivity.js";
import ClassicDiagram from "./components/pages/classic/Classic.js";
+import * as htmlToImage from "html-to-image";
+import FileSaver from "file-saver";
const withRouter = (Page) => (props) => {
const params = useParams();
@@ -91,6 +93,7 @@ class Craig extends React.Component {
clickedWorkspace: "",
clickedWorkspaceUrl: "",
current_project: craig.store.project_name,
+ showOverviewForDownload: false,
};
} catch (err) {
// if there are initialization errors, redirect user to reset state path
@@ -111,6 +114,7 @@ class Craig extends React.Component {
this.getProject = this.getProject.bind(this);
this.projectFetch = this.projectFetch.bind(this);
this.saveProject = this.saveProject.bind(this);
+ this.showAndSnapshot = this.showAndSnapshot.bind(this);
}
// when react component mounts, set update callback for store
@@ -125,6 +129,19 @@ class Craig extends React.Component {
});
}
+ showAndSnapshot(callback) {
+ this.setState({ showOverviewForDownload: true }, () => {
+ htmlToImage
+ .toBlob(document.getElementById("overview-big"))
+ .then(function (blob) {
+ callback(blob);
+ })
+ .then(() => {
+ this.setState({ showOverviewForDownload: false });
+ });
+ });
+ }
+
/**
* update components
* @param {string} message
@@ -569,6 +586,39 @@ class Craig extends React.Component {
) : (
<>
+ {this.state.showOverviewForDownload ? (
+ // this is only shown when getting an image for the screengrab,
+ // otherwise will not be rendered
+ // the first div is a container for the overview image
+ // the second div is there to mask the first while creting the image
+ <>
+
+
+
+
+ >
+ ) : (
+ ""
+ )}
{this.props.params.doc ? (
this.props.params.doc === "about" ? (
diff --git a/client/src/app.scss b/client/src/app.scss
index 7c8ad67d..5464bd43 100644
--- a/client/src/app.scss
+++ b/client/src/app.scss
@@ -397,6 +397,7 @@ input:read-only {
position: relative;
font-size: 80%;
top: 20px;
+ left: -62px;
}
.cds--popover--open .cds--popover-content {
@@ -1037,10 +1038,12 @@ div.banner-text {
}
.footerPopoverDismiss {
+ left: -143px !important; // important to override auto align
right: 450% !important; // important to override auto align
}
.footerPopoverShow {
+ left: -130px !important; // important to override auto align
right: 412.5% !important; // important to override auto align
}
@@ -2095,6 +2098,7 @@ input:read-only {
position: relative;
font-size: 80%;
top: 20px;
+ left: -62px;
}
.cds--popover--open .cds--popover-content {
diff --git a/client/src/components/forms/DynamicForm.js b/client/src/components/forms/DynamicForm.js
index fb2c379e..ab204220 100644
--- a/client/src/components/forms/DynamicForm.js
+++ b/client/src/components/forms/DynamicForm.js
@@ -6,17 +6,15 @@ import {
isFunction,
getObjectFromArray,
} from "lazy-z";
-import { forceShowForm, propsMatchState } from "../../lib";
import {
+ forceShowForm,
+ propsMatchState,
dynamicCraigFormGroupsProps,
dynamicHeadingProps,
dynamicToolTipWrapperProps,
-} from "../../lib/forms/dynamic-form-fields";
+} from "../../lib";
import { edgeRouterEnabledZones } from "../../lib/constants";
-import {
- DynamicFetchMultiSelect,
- DynamicFetchSelect,
-} from "./dynamic-form/components";
+import { DynamicFetchMultiSelect, DynamicFetchSelect } from "./dynamic-form";
import {
SubnetTileSubForm,
SubnetTileTitle,
@@ -266,9 +264,11 @@ class DynamicForm extends React.Component {
{this.props.form.groups.map((group, index) =>
- group.hideWhen && group.hideWhen(this.state) ? (
+ group.hideWhen && group.hideWhen(this.state, this.props) ? (
""
- ) : group.heading && group.hideWhen && group.hideWhen(this.state) ? (
+ ) : group.heading &&
+ group.hideWhen &&
+ group.hideWhen(this.state, this.props) ? (
""
) : group.heading ? (
diff --git a/client/src/components/forms/dynamic-form/DynamicDatePicker.js b/client/src/components/forms/dynamic-form/DynamicDatePicker.js
new file mode 100644
index 00000000..eb66f3ea
--- /dev/null
+++ b/client/src/components/forms/dynamic-form/DynamicDatePicker.js
@@ -0,0 +1,41 @@
+import React from "react";
+import { DatePicker, DatePickerInput } from "@carbon/react";
+import PropTypes from "prop-types";
+
+const DynamicDatePicker = (props) => {
+ // only used in opaque secrets, if we use this in other places we can
+ // change it to be more dynamic
+ return (
+ {
+ let event = {
+ target: {
+ name: "expiration_date",
+ value: selectEvent[0],
+ },
+ };
+ props.handleInputChange(event);
+ }}
+ >
+
+
+ );
+};
+
+DynamicDatePicker.propTypes = {
+ parentState: PropTypes.shape({
+ expiration_date: PropTypes.string,
+ }).isRequired,
+ handleInputChange: PropTypes.func.isRequired,
+};
+
+export { DynamicDatePicker };
diff --git a/client/src/components/forms/dynamic-form/DynamicFetchMultiSelect.js b/client/src/components/forms/dynamic-form/DynamicFetchMultiSelect.js
new file mode 100644
index 00000000..78a99731
--- /dev/null
+++ b/client/src/components/forms/dynamic-form/DynamicFetchMultiSelect.js
@@ -0,0 +1,74 @@
+import React from "react";
+import { FilterableMultiSelect } from "@carbon/react";
+import { dynamicMultiSelectProps } from "../../../lib";
+import PropTypes from "prop-types";
+import { deepEqual } from "lazy-z";
+
+class DynamicFetchMultiSelect extends React.Component {
+ _isMounted = false;
+ constructor(props) {
+ super(props);
+ this.state = {
+ data: ["Loading..."],
+ };
+ }
+
+ componentDidMount() {
+ this._isMounted = true;
+ // on mount if not items have been set
+ if (deepEqual(this.state.data, ["Loading..."])) {
+ fetch(
+ // generate api endpoint based on state and props
+ this.props.field.apiEndpoint(
+ this.props.parentState,
+ this.props.parentProps
+ )
+ )
+ .then((res) => res.json())
+ .then((data) => {
+ // set state with data if mounted
+ if (this._isMounted) {
+ this.setState({ data: data }, () => {
+ this.props.onPowerImageLoad(data);
+ });
+ }
+ })
+ .catch((err) => {
+ console.error(err);
+ });
+ }
+ }
+
+ componentWillUnmount() {
+ this._isMounted = false;
+ }
+
+ // Force re-fetch of images on zone change
+ componentDidUpdate(prevProps) {
+ if (prevProps.parentState.zone != this.props.parentState.zone) {
+ this._isMounted = false;
+ this.setState({ data: ["Loading..."] }, () => {
+ this.componentDidMount();
+ });
+ }
+ }
+
+ render() {
+ let props = { ...this.props };
+ return (
+
+ );
+ }
+}
+
+DynamicFetchMultiSelect.propTypes = {
+ onPowerImageLoad: PropTypes.func.isRequired,
+ field: PropTypes.shape({
+ apiEndpoint: PropTypes.func.isRequired,
+ }).isRequired,
+ parentState: PropTypes.shape({}),
+};
+
+export default DynamicFetchMultiSelect;
diff --git a/client/src/components/forms/dynamic-form/DynamicFetchSelect.js b/client/src/components/forms/dynamic-form/DynamicFetchSelect.js
new file mode 100644
index 00000000..37154ffe
--- /dev/null
+++ b/client/src/components/forms/dynamic-form/DynamicFetchSelect.js
@@ -0,0 +1,135 @@
+import React from "react";
+import PropTypes from "prop-types";
+// popover wrapper needs to be imported this way to prevent an error importing
+// dynamic form before initializtion
+import { default as PopoverWrapper } from "../utils/PopoverWrapper";
+import { contains, deepEqual, isFunction, isNullOrEmptyString } from "lazy-z";
+import { dynamicFieldId, dynamicSelectProps } from "../../../lib";
+import { Select, SelectItem } from "@carbon/react";
+
+class DynamicFetchSelect extends React.Component {
+ _isMounted = false;
+ constructor(props) {
+ super(props);
+ this.state = {
+ data: ["Loading..."],
+ };
+ this.dataToGroups = this.dataToGroups.bind(this);
+ }
+
+ componentDidMount() {
+ this._isMounted = true;
+ // on mount if not items have been set
+ if (deepEqual(this.state.data, ["Loading..."])) {
+ fetch(
+ // generate api endpoint based on state and props
+ this.props.field.apiEndpoint(
+ this.props.parentState,
+ this.props.parentProps
+ )
+ )
+ .then((res) => res.json())
+ .then((data) => {
+ // set state with data if mounted
+ if (this._isMounted) {
+ this.setState({ data: data });
+ }
+ })
+ .catch((err) => {
+ console.error(err);
+ });
+ }
+ }
+
+ componentWillUnmount() {
+ this._isMounted = false;
+ }
+
+ dataToGroups() {
+ let apiEndpoint = this.props.field.apiEndpoint(
+ this.props.parentState,
+ this.props.parentProps
+ );
+ if (apiEndpoint === "/api/cluster/versions") {
+ // add "" if kube version is reset
+ return (
+ this.props.parentProps.isModal ||
+ isNullOrEmptyString(this.props.parentState.kube_version)
+ ? [""]
+ : []
+ ).concat(
+ // filter version based on kube type
+ this.state.data.filter((version) => {
+ if (
+ (this.props.parentState.kube_type === "openshift" &&
+ contains(version, "openshift")) ||
+ (this.props.parentState.kube_type === "iks" &&
+ !contains(version, "openshift")) ||
+ version === "default"
+ ) {
+ return version.replace(/\s\(Default\)/g, "");
+ }
+ })
+ );
+ } else {
+ return (
+ // to prevent storage pools from being loaded incorrectly,
+ // prevent first item in storage groups from being loaded when not selected
+ (
+ dynamicSelectProps(this.props).value === "" &&
+ this._isMounted &&
+ !deepEqual(this.state.data, ["Loading..."])
+ ? [""]
+ : []
+ )
+ .concat(this.state.data)
+ .map((item) => {
+ if (isFunction(this.props.field.onRender)) {
+ return this.props.field.onRender({
+ [this.props.name]: item,
+ });
+ } else return item;
+ })
+ );
+ }
+ }
+
+ render() {
+ let props = { ...this.props };
+ return (
+
+
+
+ );
+ }
+}
+
+DynamicFetchSelect.propTypes = {
+ parentState: PropTypes.shape({
+ kube_version: PropTypes.string,
+ }).isRequired,
+ parentProps: PropTypes.shape({
+ isModal: PropTypes.bool,
+ }).isRequired,
+ field: PropTypes.shape({
+ apiEndpoint: PropTypes.func.isRequired,
+ onRender: PropTypes.func,
+ tooltip: PropTypes.shape({}),
+ }).isRequired,
+};
+
+export default DynamicFetchSelect;
diff --git a/client/src/components/forms/dynamic-form/PowerInterfaces.js b/client/src/components/forms/dynamic-form/PowerInterfaces.js
index 26d53f85..b025b9b1 100644
--- a/client/src/components/forms/dynamic-form/PowerInterfaces.js
+++ b/client/src/components/forms/dynamic-form/PowerInterfaces.js
@@ -2,11 +2,35 @@ import React from "react";
import PropTypes from "prop-types";
import { Network_3 } from "@carbon/icons-react";
import { DynamicFormTextInput } from "./components";
-import { contains } from "lazy-z";
+import { contains, isEmpty, isNullOrEmptyString } from "lazy-z";
import { CraigFormGroup } from "../utils";
+import { CraigEmptyResourceTile } from "./tiles";
export const PowerInterfaces = (props) => {
- return contains(["Power Instances", "VTL"], props.componentProps.formName) ? (
+ let isVtl = contains(["VTL"], props.componentProps.formName);
+ return isVtl &&
+ (!props.stateData.workspace ||
+ isEmpty(
+ props.componentProps.craig.vtl.image.groups(
+ props.stateData,
+ props.componentProps
+ )
+ )) ? (
+
+ VTL images selected in Power VS workspace{" "}
+ {props.stateData.workspace}.
+ Add images from the Power VS form
+
+ )
+ }
+ />
+ ) : contains(["Power Instances", "VTL"], props.componentProps.formName) ? (
{props.stateData.network.map((nw, index) => {
return (
diff --git a/client/src/components/forms/dynamic-form/components.js b/client/src/components/forms/dynamic-form/components.js
index 293e3b4b..ec979aad 100644
--- a/client/src/components/forms/dynamic-form/components.js
+++ b/client/src/components/forms/dynamic-form/components.js
@@ -9,7 +9,8 @@ import {
dynamicToggleProps,
dynamicTextAreaProps,
dynamicMultiSelectProps,
-} from "../../../lib/forms/dynamic-form-fields";
+ dynamicPasswordInputProps,
+} from "../../../lib";
import {
FilterableMultiSelect,
SelectItem,
@@ -18,12 +19,8 @@ import {
TextInput,
Select,
Tag,
- DatePicker,
- DatePickerInput,
} from "@carbon/react";
import PropTypes from "prop-types";
-import { dynamicPasswordInputProps } from "../../../lib/forms/dynamic-form-fields/password-input";
-import { contains, deepEqual, isFunction, isNullOrEmptyString } from "lazy-z";
import { ToolTipWrapper } from "../utils/ToolTip";
import { RenderForm } from "../utils";
@@ -224,204 +221,6 @@ DynamicPublicKey.propTypes = {
handleInputChange: PropTypes.func.isRequired,
};
-export class DynamicFetchSelect extends React.Component {
- _isMounted = false;
- constructor(props) {
- super(props);
- this.state = {
- data: ["Loading..."],
- };
- this.dataToGroups = this.dataToGroups.bind(this);
- }
-
- componentDidMount() {
- this._isMounted = true;
- // on mount if not items have been set
- if (deepEqual(this.state.data, ["Loading..."]))
- fetch(
- // generate api endpoint based on state and props
- this.props.field.apiEndpoint(
- this.props.parentState,
- this.props.parentProps
- )
- )
- .then((res) => res.json())
- .then((data) => {
- // set state with data if mounted
- if (this._isMounted) {
- this.setState({ data: data });
- }
- })
- .catch((err) => {
- console.error(err);
- });
- }
-
- componentWillUnmount() {
- this._isMounted = false;
- }
-
- dataToGroups() {
- let apiEndpoint = this.props.field.apiEndpoint(
- this.props.parentState,
- this.props.parentProps
- );
- if (apiEndpoint === "/api/cluster/versions") {
- // add "" if kube version is reset
- return (
- this.props.parentProps.isModal ||
- isNullOrEmptyString(this.props.parentState.kube_version)
- ? [""]
- : []
- ).concat(
- // filter version based on kube type
- this.state.data.filter((version) => {
- if (
- (this.props.parentState.kube_type === "openshift" &&
- contains(version, "openshift")) ||
- (this.props.parentState.kube_type === "iks" &&
- !contains(version, "openshift")) ||
- version === "default"
- ) {
- return version.replace(/\s\(Default\)/g, "");
- }
- })
- );
- } else {
- return (
- // to prevent storage pools from being loaded incorrectly,
- // prevent first item in storage groups from being loaded when not selected
- (
- dynamicSelectProps(this.props).value === "" &&
- this._isMounted &&
- !deepEqual(this.state.data, ["Loading..."])
- ? [""]
- : []
- )
- .concat(this.state.data)
- .map((item) => {
- if (isFunction(this.props.field.onRender)) {
- return this.props.field.onRender({
- [this.props.name]: item,
- });
- } else return item;
- })
- );
- }
- }
-
- render() {
- let props = { ...this.props };
- return (
-
-
-
- );
- }
-}
-
-export class DynamicFetchMultiSelect extends React.Component {
- _isMounted = false;
- constructor(props) {
- super(props);
- this.state = {
- data: ["Loading..."],
- };
- }
-
- componentDidMount() {
- this._isMounted = true;
- // on mount if not items have been set
- if (deepEqual(this.state.data, ["Loading..."])) {
- fetch(
- // generate api endpoint based on state and props
- this.props.field.apiEndpoint(
- this.props.parentState,
- this.props.parentProps
- )
- )
- .then((res) => res.json())
- .then((data) => {
- // set state with data if mounted
- if (this._isMounted) {
- this.setState({ data: data }, () => {
- this.props.onPowerImageLoad(data);
- });
- }
- })
- .catch((err) => {
- console.error(err);
- });
- }
- }
-
- componentWillUnmount() {
- this._isMounted = false;
- }
-
- // Force re-fetch of images on zone change
- componentDidUpdate(prevProps) {
- if (prevProps.parentState.zone != this.props.parentState.zone) {
- this._isMounted = false;
- this.setState({ data: ["Loading..."] }, () => {
- this.componentDidMount();
- });
- }
- }
-
- render() {
- let props = { ...this.props };
- return (
-
- );
- }
-}
-
-const DynamicDatePicker = (props) => {
- // only used in opaque secrets, if we use this in other places we can
- // change it to be more dynamic
- return (
- {
- let event = {
- target: {
- name: "expiration_date",
- value: selectEvent[0],
- },
- };
- props.handleInputChange(event);
- }}
- >
-
-
- );
-};
-
export {
DynamicFormTextInput,
DynamicFormSelect,
@@ -431,5 +230,4 @@ export {
DynamicPublicKey,
DynamicToolTipWrapper,
tagColors,
- DynamicDatePicker,
};
diff --git a/client/src/components/forms/dynamic-form/index.js b/client/src/components/forms/dynamic-form/index.js
index 9b26bbb1..095d61ba 100644
--- a/client/src/components/forms/dynamic-form/index.js
+++ b/client/src/components/forms/dynamic-form/index.js
@@ -5,11 +5,13 @@ export {
DynamicFormToggle,
DynamicTextArea,
DynamicPublicKey,
- DynamicDatePicker,
DynamicToolTipWrapper,
} from "./components";
+export { DynamicDatePicker } from "./DynamicDatePicker";
export { PowerInterfaces } from "./PowerInterfaces";
export { SubFormOverrideTile } from "./SubFormOverrideTile";
+export { default as DynamicFetchMultiSelect } from "./DynamicFetchMultiSelect";
+export { default as DynamicFetchSelect } from "./DynamicFetchSelect";
export {
ClassicDisabledTile,
NoClassicGatewaysTile,
diff --git a/client/src/components/forms/utils/PrimaryButton.js b/client/src/components/forms/utils/PrimaryButton.js
index 743e5d15..ee122a36 100644
--- a/client/src/components/forms/utils/PrimaryButton.js
+++ b/client/src/components/forms/utils/PrimaryButton.js
@@ -57,7 +57,7 @@ PrimaryButton.defaultProps = {
hoverText: "Save Changes",
inline: false,
disabled: false,
- hoverTextAlign: "bottom",
+ hoverTextAlign: "bottom-right",
};
PrimaryButton.propTypes = {
diff --git a/client/src/components/forms/utils/SecondaryButton.js b/client/src/components/forms/utils/SecondaryButton.js
index de70618d..0cad3ca7 100644
--- a/client/src/components/forms/utils/SecondaryButton.js
+++ b/client/src/components/forms/utils/SecondaryButton.js
@@ -1,12 +1,16 @@
import React from "react";
import PropTypes from "prop-types";
+import { contains } from "lazy-z";
import { default as PopoverWrapper } from "./PopoverWrapper";
import { TrashCan } from "@carbon/icons-react";
import { Button } from "@carbon/react";
import { dynamicSecondaryButtonProps } from "../../../lib/components/toggle-form-components";
export const SecondaryButton = (props) => {
- let buttonProps = dynamicSecondaryButtonProps(props);
+ let isV2Page =
+ contains(window.location.pathname, "/v2") ||
+ contains(window.location.search, "v2");
+ let buttonProps = dynamicSecondaryButtonProps(props, isV2Page);
return (