Skip to content

Commit 5ad4646

Browse files
author
stephenliberty
authored
feat: allow use of source file instead of content / content_base64 (kreuzwerker#240)
Closes kreuzwerker#239 * Added a 'source' and 'source hash' which will reference a file / file hash to load into the container * Add to docs * Adding a test, cleaning up another one
1 parent 5733f00 commit 5ad4646

6 files changed

+198
-8
lines changed

docker/resource_docker_container.go

+20
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,16 @@ func resourceDockerContainer() *schema.Resource {
716716
ForceNew: true,
717717
Default: false,
718718
},
719+
"source": {
720+
Type: schema.TypeString,
721+
Optional: true,
722+
ForceNew: true,
723+
},
724+
"source_hash": {
725+
Type: schema.TypeString,
726+
Optional: true,
727+
ForceNew: true,
728+
},
719729
},
720730
},
721731
},
@@ -1473,6 +1483,16 @@ func resourceDockerContainerV1() *schema.Resource {
14731483
ForceNew: true,
14741484
Default: false,
14751485
},
1486+
"source": {
1487+
Type: schema.TypeString,
1488+
Optional: true,
1489+
ForceNew: true,
1490+
},
1491+
"source_hash": {
1492+
Type: schema.TypeString,
1493+
Optional: true,
1494+
ForceNew: true,
1495+
},
14761496
},
14771497
},
14781498
},

docker/resource_docker_container_funcs.go

+23-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"encoding/json"
99
"errors"
1010
"fmt"
11+
"io/ioutil"
1112
"log"
1213
"os"
1314
"sort"
@@ -385,12 +386,23 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
385386
for _, upload := range v.(*schema.Set).List() {
386387
content := upload.(map[string]interface{})["content"].(string)
387388
contentBase64 := upload.(map[string]interface{})["content_base64"].(string)
388-
if content == "" && contentBase64 == "" {
389-
return fmt.Errorf("Error with upload content: neither 'content', nor 'content_base64' was set")
389+
source := upload.(map[string]interface{})["source"].(string)
390+
391+
testParams := []string{content, contentBase64, source}
392+
setParams := 0
393+
for _, v := range testParams {
394+
if v != "" {
395+
setParams++
396+
}
397+
}
398+
399+
if setParams == 0 {
400+
return fmt.Errorf("error with upload content: one of 'content', 'content_base64', or 'source' must be set")
390401
}
391-
if content != "" && contentBase64 != "" {
392-
return fmt.Errorf("Error with upload content: only one of 'content' or 'content_base64' can be specified")
402+
if setParams > 1 {
403+
return fmt.Errorf("error with upload content: only one of 'content', 'content_base64', or 'source' can be set")
393404
}
405+
394406
var contentToUpload string
395407
if content != "" {
396408
contentToUpload = content
@@ -399,6 +411,13 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
399411
decoded, _ := base64.StdEncoding.DecodeString(contentBase64)
400412
contentToUpload = string(decoded)
401413
}
414+
if source != "" {
415+
sourceContent, err := ioutil.ReadFile(source)
416+
if err != nil {
417+
return fmt.Errorf("could not read file: %s", err)
418+
}
419+
contentToUpload = string(sourceContent)
420+
}
402421
file := upload.(map[string]interface{})["file"].(string)
403422
executable := upload.(map[string]interface{})["executable"].(bool)
404423

docker/resource_docker_container_test.go

+146-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"archive/tar"
55
"bytes"
66
"fmt"
7+
"io/ioutil"
78
"os"
89
"reflect"
910
"regexp"
@@ -658,6 +659,112 @@ func TestAccDockerContainer_upload(t *testing.T) {
658659
})
659660
}
660661

662+
func TestAccDockerContainer_uploadSource(t *testing.T) {
663+
var c types.ContainerJSON
664+
665+
wd, _ := os.Getwd()
666+
testFile := wd + "/../scripts/testing/testingFile"
667+
testFileContent, _ := ioutil.ReadFile(testFile)
668+
669+
testCheck := func(*terraform.State) error {
670+
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
671+
672+
srcPath := "/terraform/test.txt"
673+
r, _, err := client.CopyFromContainer(context.Background(), c.ID, srcPath)
674+
if err != nil {
675+
return fmt.Errorf("Unable to download a file from container: %s", err)
676+
}
677+
678+
tr := tar.NewReader(r)
679+
if header, err := tr.Next(); err != nil {
680+
return fmt.Errorf("Unable to read content of tar archive: %s", err)
681+
} else {
682+
mode := strconv.FormatInt(header.Mode, 8)
683+
if !strings.HasSuffix(mode, "744") {
684+
return fmt.Errorf("File permissions are incorrect: %s", mode)
685+
}
686+
}
687+
688+
fbuf := new(bytes.Buffer)
689+
fbuf.ReadFrom(tr)
690+
content := fbuf.String()
691+
if content != string(testFileContent) {
692+
return fmt.Errorf("file content is invalid")
693+
}
694+
695+
return nil
696+
}
697+
698+
resource.Test(t, resource.TestCase{
699+
PreCheck: func() { testAccPreCheck(t) },
700+
Providers: testAccProviders,
701+
Steps: []resource.TestStep{
702+
{
703+
Config: fmt.Sprintf(testAccDockerContainerUploadSourceConfig, testFile),
704+
Check: resource.ComposeTestCheckFunc(
705+
testAccContainerRunning("docker_container.foo", &c),
706+
testCheck,
707+
resource.TestCheckResourceAttr("docker_container.foo", "name", "tf-test"),
708+
resource.TestCheckResourceAttr("docker_container.foo", "upload.#", "1"),
709+
// NOTE mavogel: current the terraform-plugin-sdk it's likely that
710+
// the acceptance testing framework shims (still using the older flatmap-style addressing)
711+
// are missing a conversion with the hashes.
712+
// See https://github.com/hashicorp/terraform-plugin-sdk/issues/196
713+
// resource.TestCheckResourceAttr("docker_container.foo", "upload.0.content", "foo"),
714+
// resource.TestCheckResourceAttr("docker_container.foo", "upload.0.content_base64", ""),
715+
// resource.TestCheckResourceAttr("docker_container.foo", "upload.0.executable", "true"),
716+
// resource.TestCheckResourceAttr("docker_container.foo", "upload.0.file", "/terraform/test.txt"),
717+
),
718+
},
719+
},
720+
})
721+
}
722+
723+
//
724+
func TestAccDockerContainer_uploadSourceHash(t *testing.T) {
725+
var c types.ContainerJSON
726+
var firstRunId string
727+
728+
wd, _ := os.Getwd()
729+
testFile := wd + "/../scripts/testing/testingFile"
730+
hash, _ := ioutil.ReadFile(testFile + ".base64")
731+
grabFirstCheck := func(*terraform.State) error {
732+
firstRunId = c.ID
733+
return nil
734+
}
735+
testCheck := func(*terraform.State) error {
736+
if c.ID == firstRunId {
737+
return fmt.Errorf("Container should have been recreated due to changed hash")
738+
}
739+
return nil
740+
}
741+
742+
resource.Test(t, resource.TestCase{
743+
PreCheck: func() { testAccPreCheck(t) },
744+
Providers: testAccProviders,
745+
Steps: []resource.TestStep{
746+
{
747+
Config: fmt.Sprintf(testAccDockerContainerUploadSourceHashConfig, testFile, string(hash)),
748+
Check: resource.ComposeTestCheckFunc(
749+
testAccContainerRunning("docker_container.foo", &c),
750+
grabFirstCheck,
751+
resource.TestCheckResourceAttr("docker_container.foo", "name", "tf-test"),
752+
resource.TestCheckResourceAttr("docker_container.foo", "upload.#", "1"),
753+
),
754+
},
755+
{
756+
Config: fmt.Sprintf(testAccDockerContainerUploadSourceHashConfig, testFile, string(hash)+"arbitrary"),
757+
Check: resource.ComposeTestCheckFunc(
758+
testAccContainerRunning("docker_container.foo", &c),
759+
testCheck,
760+
resource.TestCheckResourceAttr("docker_container.foo", "name", "tf-test"),
761+
resource.TestCheckResourceAttr("docker_container.foo", "upload.#", "1"),
762+
),
763+
},
764+
},
765+
})
766+
}
767+
661768
func TestAccDockerContainer_uploadAsBase64(t *testing.T) {
662769
var c types.ContainerJSON
663770

@@ -765,7 +872,7 @@ func TestAccDockerContainer_multipleUploadContentsConfig(t *testing.T) {
765872
}
766873
}
767874
`,
768-
ExpectError: regexp.MustCompile(`.*only one of 'content' or 'content_base64' can be specified.*`),
875+
ExpectError: regexp.MustCompile(`.*only one of 'content', 'content_base64', or 'source' can be set.*`),
769876
},
770877
},
771878
})
@@ -794,7 +901,7 @@ func TestAccDockerContainer_noUploadContentsConfig(t *testing.T) {
794901
}
795902
}
796903
`,
797-
ExpectError: regexp.MustCompile(`.* neither 'content', nor 'content_base64' was set.*`),
904+
ExpectError: regexp.MustCompile(`.* one of 'content', 'content_base64', or 'source' must be set.*`),
798905
},
799906
},
800907
})
@@ -1791,6 +1898,43 @@ resource "docker_container" "foo" {
17911898
}
17921899
`
17931900

1901+
const testAccDockerContainerUploadSourceConfig = `
1902+
resource "docker_image" "foo" {
1903+
name = "nginx:latest"
1904+
keep_locally = true
1905+
}
1906+
1907+
resource "docker_container" "foo" {
1908+
name = "tf-test"
1909+
image = "${docker_image.foo.latest}"
1910+
1911+
upload {
1912+
source = "%s"
1913+
file = "/terraform/test.txt"
1914+
executable = true
1915+
}
1916+
}
1917+
`
1918+
1919+
const testAccDockerContainerUploadSourceHashConfig = `
1920+
resource "docker_image" "foo" {
1921+
name = "nginx:latest"
1922+
keep_locally = true
1923+
}
1924+
1925+
resource "docker_container" "foo" {
1926+
name = "tf-test"
1927+
image = "${docker_image.foo.latest}"
1928+
1929+
upload {
1930+
source = "%s"
1931+
source_hash = "%s"
1932+
file = "/terraform/test.txt"
1933+
executable = true
1934+
}
1935+
}
1936+
`
1937+
17941938
const testAccDockerContainerUploadBase64Config = `
17951939
resource "docker_image" "foo" {
17961940
name = "nginx:latest"

scripts/testacc_cleanup.sh

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ set -e
44
for p in $(docker container ls -f 'name=private_registry' -q); do docker stop $p; done
55
echo "### stopped private registry ###"
66

7+
rm -f "$(pwd)/scripts/testing/testingFile"
8+
rm -f "$(pwd)/scripts/testing/testingFile.base64"
79
rm -f "$(pwd)"/scripts/testing/auth/htpasswd
810
rm -f "$(pwd)"/scripts/testing/certs/registry_auth.*
911
echo "### removed auth and certs ###"

scripts/testacc_setup.sh

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#!/bin/bash
22
set -e
33

4+
echo -n "foo" > "$(pwd)/scripts/testing/testingFile"
5+
echo -n `base64 $(pwd)/scripts/testing/testingFile` > "$(pwd)/scripts/testing/testingFile.base64"
6+
47
# Create self signed certs
58
mkdir -p "$(pwd)"/scripts/testing/certs
69
openssl req \

website/docs/r/container.html.markdown

+4-2
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,10 @@ files to upload to the container before starting it. Only one of `content` or `c
220220
one of them hast to be set.
221221
Each `upload` supports the following
222222

223-
* `content` - (Optional, string, conflicts with `content_base64`) Literal string value to use as the object content, which will be uploaded as UTF-8-encoded text.
224-
* `content_base64` - (Optional, string, conflicts with `content`) Base64-encoded data that will be decoded and uploaded as raw bytes for the object content. This allows safely uploading non-UTF8 binary data, but is recommended only for larger binary content such as the result of the `base64encode` interpolation function. See [here](https://github.com/terraform-providers/terraform-provider-docker/issues/48#issuecomment-374174588) for the reason.
223+
* `content` - (Optional, string, conflicts with `content_base64` & `source`) Literal string value to use as the object content, which will be uploaded as UTF-8-encoded text.
224+
* `content_base64` - (Optional, string, conflicts with `content` & `source`) Base64-encoded data that will be decoded and uploaded as raw bytes for the object content. This allows safely uploading non-UTF8 binary data, but is recommended only for larger binary content such as the result of the `base64encode` interpolation function. See [here](https://github.com/terraform-providers/terraform-provider-docker/issues/48#issuecomment-374174588) for the reason.
225+
* `source` - (Optional, string, conflicts with `content` & `content_base64`) A filename that references a file which will be uploaded as the object content. This allows for large file uploads that do not get stored in state.
226+
* `source_hash` - (Optional, string) If using `source`, this will force an update if the file content has updated but the filename has not.
225227
* `file` - (Required, string) path to a file in the container.
226228
* `executable` - (Optional, bool) If true, the file will be uploaded with user
227229
executable permission.

0 commit comments

Comments
 (0)