From d67665b98d37269ae2812b86c825a5686c6c9d92 Mon Sep 17 00:00:00 2001 From: Amund Tenstad Date: Mon, 22 Aug 2022 10:12:26 +0200 Subject: [PATCH] feat: timeout --- GNUmakefile | 5 ++++- README.md | 2 ++ docs/data-sources/file.md | 1 + docs/index.md | 1 + docs/resources/file.md | 1 + go.mod | 1 + internal/provider/connection.go | 16 ++++++++++++++-- internal/provider/data_source_remote_file.go | 2 +- internal/provider/provider.go | 10 +++++----- internal/provider/resource_remote_file.go | 6 +++--- internal/provider/resource_remote_file_test.go | 1 + 11 files changed, 34 insertions(+), 12 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 7829d9e..76d03a4 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -18,7 +18,7 @@ clean: # Run acceptance tests test: hosts $(CONTAINER_RUNTIME) run --rm --net remote -v ~/go:/go:z -v $(PWD):/provider:z --workdir /provider \ - -e "TF_ACC=1" -e "TF_ACC_TERRAFORM_VERSION=1.0.11" -e "TESTARGS=$(TESTARGS)" \ + -e "TF_LOG=INFO" -e "TF_ACC=1" -e "TF_ACC_TERRAFORM_VERSION=1.0.11" -e "TESTARGS=$(TESTARGS)" \ golang:1.16 bash tests/test.sh # Install provider in playground @@ -29,3 +29,6 @@ BIN_PATH=$(INSTALL_DIR)/$(PROVIDER_PATH)/terraform-provider-remote_v99.0.0 install: mkdir -p $(INSTALL_DIR)/$(PROVIDER_PATH) go build -ldflags="-s -w -X main.version=99.0.0" -o $(BIN_PATH) + +doc: + go generate diff --git a/README.md b/README.md index 44d2744..f4bdfae 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ In `playground/`: - `make install` to compile and install the provider in the playground - `make hosts` to start containers to use as remote hosts +- Optionally use `export TF_LOG=INFO` and + `tflog.Info(ctx, "message from provider")` to log from the provider. - Evaluate your changes my modifying `main.tf` and running `terraform plan` or `terraform apply` - `docker exec -it remotehost sh` to enter remote host diff --git a/docs/data-sources/file.md b/docs/data-sources/file.md index 366a87d..769a8e3 100644 --- a/docs/data-sources/file.md +++ b/docs/data-sources/file.md @@ -73,5 +73,6 @@ Optional: - `private_key_env_var` (String) The name of the local environment variable containing the private key used to login to the remote host. - `private_key_path` (String) The local path to the private key used to login to the remote host. - `sudo` (Boolean) Use sudo to gain access to file. Defaults to `false`. +- `timeout` (Number) The maximum amount of time, in milliseconds, for the TCP connection to establish. Timeout of zero means no timeout. diff --git a/docs/index.md b/docs/index.md index f2e7368..feb1d2c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -72,3 +72,4 @@ Optional: - `private_key_env_var` (String) The name of the local environment variable containing the private key used to login to the remote host. - `private_key_path` (String) The local path to the private key used to login to the remote host. - `sudo` (Boolean) Use sudo to gain access to file. Defaults to `false`. +- `timeout` (Number) The maximum amount of time, in milliseconds, for the TCP connection to establish. Timeout of zero means no timeout. diff --git a/docs/resources/file.md b/docs/resources/file.md index 41c9694..a4fcbdb 100644 --- a/docs/resources/file.md +++ b/docs/resources/file.md @@ -79,5 +79,6 @@ Optional: - `private_key_env_var` (String) The name of the local environment variable containing the private key used to login to the remote host. - `private_key_path` (String) The local path to the private key used to login to the remote host. - `sudo` (Boolean) Use sudo to gain access to file. Defaults to `false`. +- `timeout` (Number) The maximum amount of time, in milliseconds, for the TCP connection to establish. Timeout of zero means no timeout. diff --git a/go.mod b/go.mod index dd87c49..72ef08c 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.16 require ( github.com/bramvdbogaerde/go-scp v0.0.0-20210327204631-70ee53679fc9 github.com/hashicorp/terraform-plugin-docs v0.10.1 + github.com/hashicorp/terraform-plugin-log v0.4.0 // indirect github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0 github.com/pkg/sftp v1.13.5 golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e diff --git a/internal/provider/connection.go b/internal/provider/connection.go index 0858323..34bf78e 100644 --- a/internal/provider/connection.go +++ b/internal/provider/connection.go @@ -1,10 +1,12 @@ package provider import ( + "context" "fmt" "io/ioutil" "net" "os" + "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "golang.org/x/crypto/ssh" @@ -65,11 +67,16 @@ var connectionSchemaResource = &schema.Resource{ Default: false, Description: "Use a local SSH agent to login to the remote host.", }, - + "timeout": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Description: "The maximum amount of time, in milliseconds, for the TCP connection to establish. Timeout of zero means no timeout.", + }, }, } -func ConnectionFromResourceData(d *schema.ResourceData) (string, *ssh.ClientConfig, error) { +func ConnectionFromResourceData(ctx context.Context, d *schema.ResourceData) (string, *ssh.ClientConfig, error) { _, ok := d.GetOk("conn") if !ok { return "", nil, fmt.Errorf("resouce does not have a connection configured") @@ -126,6 +133,11 @@ func ConnectionFromResourceData(d *schema.ResourceData) (string, *ssh.ClientConf clientConfig.Auth = append(clientConfig.Auth, ssh.PublicKeysCallback(agent.NewClient(connection).Signers)) } + timeout, ok := d.GetOk("conn.0.timeout") + if ok { + clientConfig.Timeout = time.Duration(timeout.(int)) * time.Millisecond + } + host := fmt.Sprintf("%s:%d", d.Get("conn.0.host").(string), d.Get("conn.0.port").(int)) return host, &clientConfig, nil } diff --git a/internal/provider/data_source_remote_file.go b/internal/provider/data_source_remote_file.go index 656f1d1..e39d571 100644 --- a/internal/provider/data_source_remote_file.go +++ b/internal/provider/data_source_remote_file.go @@ -59,7 +59,7 @@ func dataSourceRemoteFileRead(ctx context.Context, d *schema.ResourceData, meta setResourceID(d, conn) - client, err := meta.(*apiClient).getRemoteClient(conn) + client, err := meta.(*apiClient).getRemoteClient(ctx, conn) if err != nil { return diag.Errorf("unable to open remote client: %s", err.Error()) } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index a767444..7fd7741 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -91,7 +91,7 @@ func (c *apiClient) getConnWithDefault(d *schema.ResourceData) (*schema.Resource c.mux.Lock() defer c.mux.Unlock() - + _, ok = c.resourceData.GetOk("conn") if ok { return c.resourceData, nil @@ -100,7 +100,7 @@ func (c *apiClient) getConnWithDefault(d *schema.ResourceData) (*schema.Resource return nil, errors.New("neither the provider nor the resource/data source have a configured connection") } -func (c *apiClient) getRemoteClient(d *schema.ResourceData) (*RemoteClient, error) { +func (c *apiClient) getRemoteClient(ctx context.Context, d *schema.ResourceData) (*RemoteClient, error) { connectionID := resourceConnectionHash(d) defer c.mux.Unlock() for { @@ -117,7 +117,7 @@ func (c *apiClient) getRemoteClient(d *schema.ResourceData) (*RemoteClient, erro return client, nil } - client, err := remoteClientFromResourceData(d) + client, err := remoteClientFromResourceData(ctx, d) if err != nil { return nil, err } @@ -128,8 +128,8 @@ func (c *apiClient) getRemoteClient(d *schema.ResourceData) (*RemoteClient, erro } } -func remoteClientFromResourceData(d *schema.ResourceData) (*RemoteClient, error) { - host, clientConfig, err := ConnectionFromResourceData(d) +func remoteClientFromResourceData(ctx context.Context, d *schema.ResourceData) (*RemoteClient, error) { + host, clientConfig, err := ConnectionFromResourceData(ctx, d) if err != nil { return nil, err } diff --git a/internal/provider/resource_remote_file.go b/internal/provider/resource_remote_file.go index a7d4020..8ead650 100644 --- a/internal/provider/resource_remote_file.go +++ b/internal/provider/resource_remote_file.go @@ -64,7 +64,7 @@ func resourceRemoteFileCreate(ctx context.Context, d *schema.ResourceData, meta setResourceID(d, conn) - client, err := meta.(*apiClient).getRemoteClient(conn) + client, err := meta.(*apiClient).getRemoteClient(ctx, conn) if err != nil { return diag.Errorf("unable to open remote client: %s", err.Error()) } @@ -117,7 +117,7 @@ func resourceRemoteFileRead(ctx context.Context, d *schema.ResourceData, meta in setResourceID(d, conn) - client, err := meta.(*apiClient).getRemoteClient(conn) + client, err := meta.(*apiClient).getRemoteClient(ctx, conn) if err != nil { return diag.Errorf("unable to open remote client: %s", err.Error()) } @@ -182,7 +182,7 @@ func resourceRemoteFileDelete(ctx context.Context, d *schema.ResourceData, meta return diag.Errorf(err.Error()) } - client, err := meta.(*apiClient).getRemoteClient(conn) + client, err := meta.(*apiClient).getRemoteClient(ctx, conn) if err != nil { return diag.Errorf("unable to open remote client: %s", err.Error()) } diff --git a/internal/provider/resource_remote_file_test.go b/internal/provider/resource_remote_file_test.go index ae62810..414b71e 100644 --- a/internal/provider/resource_remote_file_test.go +++ b/internal/provider/resource_remote_file_test.go @@ -21,6 +21,7 @@ func TestAccResourceRemoteFile(t *testing.T) { user = "root" sudo = true password = "password" + timeout = 1000 } path = "/tmp/resource_1.txt" content = "resource_1"