github.com/darmach/terratest@v0.34.8-0.20210517103231-80931f95e3ff/docs/_docs/04_community/contributing.md (about)

     1  ---
     2  layout: collection-browser-doc
     3  title: Contributing
     4  category: community
     5  excerpt: >-
     6    Terratest is an open source project, and contributions from the community are very welcome!
     7  tags: ["contributing", "community"]
     8  order: 400
     9  nav_title: Documentation
    10  nav_title_link: /docs/
    11  custom_js:
    12    - examples
    13    - prism
    14    - collection-browser_scroll
    15    - collection-browser_search
    16    - collection-browser_toc
    17  ---
    18  
    19  Terratest is an open source project, and contributions from the community are very welcome\! Please check out the
    20  [Contribution Guidelines](#contribution-guidelines) and [Developing Terratest](#developing-terratest) for
    21  instructions.
    22  
    23  ## Contribution Guidelines
    24  
    25  Contributions to this repo are very welcome! We follow a fairly standard [pull request
    26  process](https://help.github.com/articles/about-pull-requests/) for contributions, subject to the following guidelines:
    27  
    28  1. [Types of contributions](#types-of-contributions)
    29  1. [File a GitHub issue](#file-a-github-issue)
    30  1. [Update the documentation](#update-the-documentation)
    31  1. [Update the tests](#update-the-tests)
    32  1. [Update the code](#update-the-code)
    33  1. [Create a pull request](#create-a-pull-request)
    34  1. [Merge and release](#merge-and-release)
    35  
    36  ### Types of contributions
    37  
    38  Broadly speaking, Terratest contains two types of helper functions:
    39  
    40  1. Integrations with external tools
    41  1. Infrastructure and validation helpers
    42  
    43  We accept different types of contributions for each of these two types of helper functions, as described next.
    44  
    45  #### Integrations with external tools
    46  
    47  These are helper functions that integrate with various DevOps tools—e.g., Terraform, Docker, Packer, and
    48  Kubernetes—that you can use to deploy infrastructure in your automated tests. Examples:
    49  
    50  * `terraform.InitAndApply`: run `terraform init` and `terraform apply`.
    51  * `packer.BuildArtifacts`: run `packer build`.
    52  * `shell.RunCommandAndGetOutput`: run an arbitrary shell command and return `stdout` and `stderr` as a string.
    53  
    54  Here are the guidelines for contributions with external tools:
    55  
    56  1. **Fixes and improvements to existing integrations**: All bug fixes and new features for existing tool integrations
    57     are very welcome!  
    58  
    59  1. **New integrations**: Before contributing an integration with a totally new tool, please file a GitHub issue to
    60     discuss with us if it's something we are interested in supporting and maintaining. For example, we may be open to
    61     new integrations with Docker and Kubernetes tools, but we may not be open to integrations with Chef or Puppet, as
    62     there are already testing tools available for them.
    63  
    64  #### Infrastructure and validation helpers
    65  
    66  These are helper functions for creating, destroying, and validating infrastructure directly via API calls or SDKs.
    67  Examples:
    68  
    69  * `http_helper.HttpGetWithRetry`: make an HTTP request, retrying until you get a certain expected response.
    70  * `ssh.CheckSshCommand`: SSH to a server and execute a command.
    71  * `aws.CreateS3Bucket`: create an S3 bucket.
    72  * `aws.GetPrivateIpsOfEc2Instances`:  use the AWS APIs to fetch IPs of some EC2 instances.
    73  
    74  The number of possible such helpers is nearly infinite, so to avoid Terratest becoming a gigantic, sprawling library
    75  we ask that contributions for new infrastructure helpers are limited to:
    76  
    77  1. **Platforms**: we currently only support three major public clouds (AWS, GCP, Azure) and Kubernetes. There is some
    78     code contributed earlier for other platforms (e.g., OCI), but until we have the time/resources to support those
    79     platforms fully, we will only accept contributions for the major public clouds and Kubernetes.
    80  
    81  1. **Complexity**: we ask that you only contribute infrastructure and validation helpers for code that is relatively
    82     complex to do from scratch. For example, a helper that merely wraps an existing function in the AWS or GCP SDK is
    83     not a great choice, as the wrapper isn't contributing much value, but is bloating the Terratest API. On the other
    84     hand, helpers that expose simple APIs for complex logic are great contributions: `ssh.CheckSshCommand` is a great
    85     example of this, as it provides a simple one-line interface for dozens of lines of complicated SSH logic.
    86  
    87  1. **Popularity**: Terratest should only contain helpers for common use cases that come up again and again in the
    88     course of testing. We don't want to bloat the library with lots of esoteric helpers for rarely used tools, so
    89     here's a quick litmus test: (a) Is this helper something you've used once or twice in your own tests, or is it
    90     something you're using over and over again? (b) Does this helper only apply to some use case specific to your
    91     company or is it likely that many other Terratest users are hitting this use case over and over again too?
    92  
    93  1. **Creating infrastructure**: we try to keep helper functions that create infrastructure (e.g., use the AWS SDK to
    94     create an S3 bucket or EC2 instance) to a minimum, as those functions typically require maintaining state (so that
    95     they are idempotent and can clean up that infrastructure at the end of the test) and dealing with asynchronous and
    96     eventually consistent cloud APIs. This can be surprisingly complicated, so we typically recommend using a tool like
    97     Terraform, which already handles all that complexity, to create any infrastructure you need at test time, and
    98     running Terratest's built-in `terraform` helpers as necessary. If you're considering contributing a function that
    99     creates infrastructure directly (e.g., using a cloud provider's APIs), please file a GitHub issue to explain why
   100     such a function would be a better choice than using a tool like Terraform.
   101  
   102  ### File a GitHub issue
   103  
   104  Before starting any work, we recommend filing a GitHub issue in this repo. This is your chance to ask questions and
   105  get feedback from the maintainers and the community before you sink a lot of time into writing (possibly the wrong)
   106  code. If there is anything you're unsure about, just ask!
   107  
   108  ### Update the documentation
   109  
   110  We recommend updating the documentation *before* updating any code (see [Readme Driven
   111  Development](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html)). This ensures the documentation
   112  stays up to date and allows you to think through the problem at a high level before you get lost in the weeds of
   113  coding.
   114  
   115  The documentation is built with Jekyll and hosted on the Github Pages from `docs` folder on `master` branch. Check out [Terratest website](https://github.com/gruntwork-io/terratest/tree/master/docs#working-with-the-documentation) to learn more about working with the documentation.
   116  
   117  ### Update the tests
   118  
   119  We also recommend updating the automated tests *before* updating any code (see [Test Driven
   120  Development](https://en.wikipedia.org/wiki/Test-driven_development)). That means you add or update a test case,
   121  verify that it's failing with a clear error message, and *then* make the code changes to get that test to pass. This
   122  ensures the tests stay up to date and verify all the functionality in this Module, including whatever new
   123  functionality you're adding in your contribution. The instructions for running the automated tests can be
   124  found [here](https://terratest.gruntwork.io/docs/community/contributing/#developing-terratest).
   125  
   126  ### Update the code
   127  
   128  At this point, make your code changes and use your new test case to verify that everything is working. As you work,
   129  please make every effort to avoid unnecessary backwards incompatible changes. This generally means that you should
   130  not delete or rename anything in a public API.
   131  
   132  If a backwards incompatible change cannot be avoided, please make sure to call that out when you submit a pull request,
   133  explaining why the change is absolutely necessary.
   134  
   135  Note that we use pre-commit hooks with this project. To ensure they run:
   136  
   137  1. Install [pre-commit](https://pre-commit.com/).
   138  1. Run `pre-commit install`.
   139  
   140  One of the pre-commit hooks we run is [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports). To prevent the
   141  hook from failing, make sure to :
   142  
   143  1. Install [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports)
   144  1. Run `goimports -w .`.
   145  
   146  We have a [style guide](https://gruntwork.io/guides/style%20guides/golang-style-guide/) for the Go programming language,
   147  in which we documented some best practices for writing Go code. Please ensure your code adheres to the guidelines
   148  outlined in the guide.
   149  
   150  ### Create a pull request
   151  
   152  [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) with your changes. Please make sure
   153  to include the following:
   154  
   155  1. A description of the change, including a link to your GitHub issue.
   156  1. The output of your automated test run, preferably in a [GitHub Gist](https://gist.github.com/). We cannot run
   157     automated tests for pull requests automatically due to [security
   158     concerns](https://circleci.com/docs/2.0/oss/#security), so we need you to manually provide this
   159     test output so we can verify that everything is working.
   160  1. Any notes on backwards incompatibility or downtime.
   161  
   162  #### Validate the Pull Request for Azure Platform
   163  
   164  If you're contributing code for the [Azure Platform](https://azure.com) and if you have an active _Azure subscription_, it's recommended to follow the below guidelines after [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). If you're contributing code for any other platform (e.g., AWS, GCP, etc), you can skip these steps.
   165  
   166  > Once the Terratest maintainers add `Azure` tag and _Approve_ the PR, following pipeline will run automatically to perform a full validation of the Azure contribution. You also can run the pipeline manually on your forked repo by following the below guideline.
   167  
   168  
   169  We have a separate CI pipeline for _Azure_ code. To run it on a forked repo:
   170  
   171  1. Run the following [Azure Cli](https://docs.microsoft.com/cli/azure/) command on your preferred Terminal to create Azure credentials and copy the output:
   172  
   173      ```bash
   174      az ad sp create-for-rbac --name "terratest-az-cli" --role contributor --sdk-auth
   175      ```
   176  
   177  1. Go to Secrets settings page under `Settings` tab in your forked project, `https://github.com/<YOUR_GITHUB_ACCOUNT>/terratest/settings`, on GitHub.
   178  
   179  1. Create a new `Secret` named `AZURE_CREDENTIALS` and paste the Azure credentials you copied from the 1<sup>st</sup> step as the value
   180  
   181      > `AZURE_CREDENTIALS` will be stored in _your_ GitHub account; neither the Terratest maintainers nor anyone else will have any access to it. Under the hood, GitHub stores your secrets in a secure, encrypted format (see: [GitHub Actions Secrets Reference](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets) for more information). Once the secret is created, it's only possible to update or delete it; the value of the secret can't be viewed. GitHub uses a [libsodium sealed box](https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes) to help ensure that secrets are encrypted before they reach GitHub.
   182  
   183  1. Create a [new Personal Access Token (PAT)](https://github.com/settings/tokens/new) page under [Settings](https://github.com/settings/profile) / [Developer Settings](https://github.com/settings/apps), making sure `write:discussion` and `public_repo` scopes are checked. Click the _Generate token_ button and copy the generated PAT.
   184  
   185  1. Go back to settings/secrets in your fork and [Create a new Secret](https://docs.github.com/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository) named `PAT`.  Paste the output from the 4<sup>th</sup> step as the value
   186  
   187      > `PAT` will be stored in _your_ GitHub account; neither the Terratest maintainers nor anyone else will have any access to it. Under the hood, GitHub stores your secrets in a secure, encrypted format (see: [GitHub Actions Secrets Reference](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets) for more information). Once the secret is created, it's only possible to update or delete it; the value of the secret can't be viewed. GitHub uses a [libsodium sealed box](https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes) to help ensure that secrets are encrypted before they reach GitHub.
   188  
   189  1. Go to Actions tab on GitHub (https://github.com/<GITHUB_ACCOUNT>/terratest/actions)
   190  
   191  1. Click `ci-workflow` workflow
   192  
   193  1. Click `Run workflow` button and fill the fields in the drop down
   194      * _Repository Info_ : name of the forked repo (_e.g. xyz/terratest_)
   195      * _Name of the branch_ : branch name on the forked repo (_e.g. feature/adding-some-important-module_)
   196      * _Name of the official terratest repo_ : home of the target pr (_gruntwork-io/terratest_)
   197      * PR number on the official terratest repo : pr number on the official terratest repo (_e.g. 14, 25, etc._).  Setting this value will leave a success/failure comment in the PR once CI completes execution.
   198  
   199      * Skip provider registration : set true if you want to skip terraform provider registration for debug purposes (_false_ or _true_)
   200  
   201  1. Wait for the `ci-workflow` to be finished
   202  
   203      > The pipeline will use the given Azure subscription and deploy real resources in your Azure account as part of running the test. When the tests finish, they will tear down the resources they created. Of course, if there is a bug or glitch that prevents the clean up code from running, some resources may be left behind, but this is rare. Note that these resources may cost you money! You are responsible for all charges in your Azure subscription.
   204  
   205  1. PR with the given _PR Number_ will have the result of the `ci-workflow` as a comment
   206  
   207  ### Merge and release
   208  
   209  The maintainers for this repo will review your code and provide feedback. Once the PR is accepted, they will merge the
   210  code and release a new version, which you'll be able to find in the [releases page](https://github.com/gruntwork-io/terratest/releases).
   211  
   212  ## Developing Terratest
   213  
   214  1. [Running tests](#running-tests)
   215  1. [Versioning](#versioning)
   216  1. [Developing For Azure](#developing-for-azure)
   217  
   218  ### Running tests
   219  
   220  Terratest itself includes a number of automated tests.
   221  
   222  **Note #1**: Some of these tests create real resources in an AWS account. That means they cost money to run, especially
   223  if you don't clean up after yourself. Please be considerate of the resources you create and take extra care to clean
   224  everything up when you're done!
   225  
   226  **Note #2**: In order to run tests that access your AWS account, you will need to configure your [AWS CLI
   227  credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html). For example, you could
   228  set the credentials as the environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
   229  
   230  **Note #3**: Never hit `CTRL + C` or cancel a build once tests are running or the cleanup tasks won't run!
   231  
   232  **Prerequisite**: The tests expect Terraform, Terragrunt, Packer, and/or Docker to already be installed and in your `PATH`.
   233  
   234  To run all the tests:
   235  
   236  ```bash
   237  go test -v -timeout 30m -p 1 ./...
   238  ```
   239  
   240  To run the tests in a specific folder:
   241  
   242  ```bash
   243  cd "<FOLDER_PATH>"
   244  go test -timeout 30m
   245  ```
   246  
   247  To run a specific test in a specific folder:
   248  
   249  ```bash
   250  cd "<FOLDER_PATH>"
   251  go test -timeout 30m -run "<TEST_NAME>"
   252  ```
   253  
   254  ### Versioning
   255  
   256  This repo follows the principles of [Semantic Versioning](http://semver.org/). You can find each new release,
   257  along with the changelog, in the [Releases Page](https://github.com/gruntwork-io/terratest/releases).
   258  
   259  During initial development, the major version will be 0 (e.g., `0.x.y`), which indicates the code does not yet have a
   260  stable API. Once we hit `1.0.0`, we will make every effort to maintain a backwards compatible API and use the MAJOR,
   261  MINOR, and PATCH versions on each release to indicate any incompatibilities.
   262  
   263  ### Developing For Azure
   264  
   265  Azure supports multliple cloud environments. In order to properly register the correct environment for you test code, you need to use the Azure SDK Client Factory.
   266  
   267  #### Azure SDK Client Factory
   268  
   269  This documentation provides and overview of the `client_factory.go` module, targeted use cases, and behaviors.  This module is intended to provide support for and simplify working with Azure's multiple cloud environments (Azure Public, Azure Government, Azure China, Azure Germany and Azure Stack).  Developers looking to contribute to additional support for Azure to Terratest should leverage client_factory and use the patterns below to add a resource REST client from Azure Go SDK.  By doing so, it provides a consistent means for developers using Terratest to test their Azure Infrastructure to connect to the correct cloud and its associated REST apis.
   270  
   271  ##### Background
   272  
   273  The Azure REST APIs support both Public and sovereign cloud environments (at the moment this includes Public, US Government, Germany, China, and Azure Stack environments).  If you are interacting with an environment other than public cloud, you need to set the base URI for the Azure REST API you are interacting with.
   274  
   275  ###### Base URI
   276  
   277  You must use the correct base URI's for the Azure REST API's (either directly or via Azure SDK for GO) to communicate with a cloud environment other than Azure Public. The Azure Go SDK supports this by using the `WithBaseURI` suffixed calls when creating service clients. For example, when using the `VirtualMachinesClient` with the public cloud, a developer would normally write code for the public cloud like so:
   278  
   279  ```go
   280  import (
   281      "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
   282  )
   283  
   284  func SomeVMHelperMethod() {
   285      subscriptionID := "your subscription ID"
   286  
   287      // Create a VM client and return
   288      vmClient, err := compute.NewVirtualMachinesClient(subscriptionID)
   289  
   290      // Use client / etc
   291  }
   292  ```
   293  
   294  However, this code will not work in non-Public cloud environments as the REST endpoints have different URIs depending on environment.  Instead, you need to use an alternative method (provided in the Azure REST SDK for Go) to get a properly configured client (*all REST API clients should support this alternate method*):
   295  
   296  ```go
   297  import (
   298      "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
   299  )
   300  
   301  func SomeVMHelperMethod() {
   302      subscriptionID := "your subscription ID"
   303      baseURI := "management.azure.com"
   304  
   305      // Create a VM client and return
   306      vmClient, err := compute.NewVirtualMachinesClientWithBaseURI(baseURI, subscriptionID)
   307  
   308      // Use client / etc
   309  }
   310  ```
   311  
   312  Using code similar to above, you can communicate with any Azure cloud environment just by changing the base URI that is passed to the clients (Azure Public shown in above example).
   313  
   314  ##### Lookup Environment Metadata
   315  
   316  Developers MUST avoid hardcoding these base URI's.  Instead, they should be looked up from an authoritative source. The AutoRest-GO library (used by the Go SDK) provides such functionality. The `client_factory` module makes use of the AutoRest `EnvironmentFromName(envName string)` function to return the appropriate structure.  This method and Environment structure is documented on GoDoc [here](https://godoc.org/github.com/Azure/go-autorest/autorest/azure#EnvironmentFromName).
   317  
   318  To configure different cloud environments, we will use the same `AZURE_ENVIRONMENT` environment variable that the Go SDK uses. This can currently be set to one of the following values:
   319  
   320  |Value                      |Cloud Environment  |
   321  |---------------------------|-------------------|
   322  |"AzureChinaCloud"          |ChinaCloud         |
   323  |"AzureGermanCloud"         |GermanCloud        |
   324  |"AzurePublicCloud"         |PublicCloud        |
   325  |"AzureUSGovernmentCloud"   |USGovernmentCloud  |
   326  |"AzureStackCloud"          |Azure stack        |
   327  
   328  When using the "AzureStackCloud" setting, you MUST also set the `AZURE_ENVIRONMENT_FILEPATH` variable to point to a JSON file containing your Azure Stack URI details.
   329  
   330  ##### Putting it all together
   331  
   332   `client_factory` implements this pattern described above in order to instantiate and return properly configured *REST SDK for GO* clients so that test implementers don't have to consider REST API client implementation as long as they have the correct `AZURE_ENVIRONMENT` env setting.  If this environment variable is not set, the client will assume public cloud as the cloud environment to communicate with.  We strongly recommend developers creating Terratest helper methods for Azure use this pattern with client factory to create REST API clients.  This will reduce effort for Terratest users creating test for Azure resources.
   333  
   334  Note the following:
   335  
   336  * TERRAFORM uses [ARM_ENVIRONMENT](https://www.terraform.io/docs/backends/types/azurerm.html#environment) environment variable to set the correct cloud environment.  
   337  * The default behavior of the `client_factory` is to use the AzurePublicCloud environment. This requires no work from the developer to configure, and ensures consistent behavior with the current SDK code.
   338  
   339  ###### Wait, I don't see the client in client factory for the rest api I want to interact with
   340  
   341   If you require a client that is not already implemented in client factory for your helper method, you will need to create a corresponding method that instantiates the client and accepts base URI following the patterns discussed.  Below is a walkthrough for adding a client to client factory.
   342  
   343  ##### Walkthrough, adding a client to client_factory
   344  
   345  ###### Add your client namespace to client factory
   346  
   347  In the Azure SDK for GO, each service should have a module that implements that services client.  You can find the correct module [here](https://godoc.org/github.com/Azure/azure-sdk-for-go).  Add that module to the client factory imports.  Below is an example for client imports that shows clients for compute, container service and subscriptions.
   348  
   349  {% include examples/explorer.html example_id='client-factory' file_id='client_factory_code' class='wide quick-start-examples' skip_learn_more=true skip_view_on_github=true skip_tags=true snippet_id='client_factory_example.imports' %}
   350  
   351  ###### Add your client method to instantiate the client
   352  
   353  The next step is to add your method to instantiate the client.  Below is an example of adding the method to create a client for Virtual Machines, note that we lookup the environment using `getEnvironmentEndpointE` and then pass that base URI to the actual method on the Virtual Machines Module to create the client `NewVirtualMachinesClientWithBaseURI`.
   354  
   355  {% include examples/explorer.html example_id='client-factory' file_id='client_factory_code' class='wide quick-start-examples' skip_learn_more=true skip_view_on_github=true skip_tags=true snippet_id='client_factory_example.CreateClient' %}
   356  
   357  ###### Add a unit test to client_factory_test.go
   358  
   359  In order to ensure that your CreateClient method works properly, add a unit test to `client_factory_test.go`.  The unit test MUST assert that the base URI is correctly set for your client.  Some key points for writing your unit test are:
   360  
   361  - Use table-driven testing to test the various combinations of cloud environments
   362  - Give the test case a descriptive name so it is easy to identify which test failed.
   363  - PRs will be rejected if a client is added without a corresponding unit test.
   364  
   365  Below is an example of the Virtual Machines client unit test:
   366  
   367  {% include examples/explorer.html example_id='client-factory' file_id='client_factory_test' class='wide quick-start-examples' skip_learn_more=true skip_view_on_github=true skip_tags=true snippet_id='client_factory_example.UnitTest' %}
   368  
   369  ###### Use your CreateClient method in your helper
   370  
   371  We now can use this client creation method in our helpers to create a Virtual Machines client.  Below is an example for how to call into this create method from `client_factory`:
   372  
   373  {% include examples/explorer.html example_id='client-factory' file_id='client_factory_helper' class='wide quick-start-examples' skip_learn_more=true skip_view_on_github=true skip_tags=true snippet_id='client_factory_example.helper' %}