config_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. package runconfig
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "runtime"
  8. "strings"
  9. "testing"
  10. "github.com/docker/docker/api/types/container"
  11. networktypes "github.com/docker/docker/api/types/network"
  12. "github.com/docker/docker/api/types/strslice"
  13. "github.com/stretchr/testify/assert"
  14. "github.com/stretchr/testify/require"
  15. )
  16. type f struct {
  17. file string
  18. entrypoint strslice.StrSlice
  19. }
  20. func TestDecodeContainerConfig(t *testing.T) {
  21. var (
  22. fixtures []f
  23. image string
  24. )
  25. if runtime.GOOS != "windows" {
  26. image = "ubuntu"
  27. fixtures = []f{
  28. {"fixtures/unix/container_config_1_14.json", strslice.StrSlice{}},
  29. {"fixtures/unix/container_config_1_17.json", strslice.StrSlice{"bash"}},
  30. {"fixtures/unix/container_config_1_19.json", strslice.StrSlice{"bash"}},
  31. }
  32. } else {
  33. image = "windows"
  34. fixtures = []f{
  35. {"fixtures/windows/container_config_1_19.json", strslice.StrSlice{"cmd"}},
  36. }
  37. }
  38. for _, f := range fixtures {
  39. b, err := ioutil.ReadFile(f.file)
  40. if err != nil {
  41. t.Fatal(err)
  42. }
  43. c, h, _, err := decodeContainerConfig(bytes.NewReader(b))
  44. if err != nil {
  45. t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
  46. }
  47. if c.Image != image {
  48. t.Fatalf("Expected %s image, found %s\n", image, c.Image)
  49. }
  50. if len(c.Entrypoint) != len(f.entrypoint) {
  51. t.Fatalf("Expected %v, found %v\n", f.entrypoint, c.Entrypoint)
  52. }
  53. if h != nil && h.Memory != 1000 {
  54. t.Fatalf("Expected memory to be 1000, found %d\n", h.Memory)
  55. }
  56. }
  57. }
  58. // TestDecodeContainerConfigIsolation validates isolation passed
  59. // to the daemon in the hostConfig structure. Note this is platform specific
  60. // as to what level of container isolation is supported.
  61. func TestDecodeContainerConfigIsolation(t *testing.T) {
  62. // An Invalid isolation level
  63. if _, _, _, err := callDecodeContainerConfigIsolation("invalid"); err != nil {
  64. if !strings.Contains(err.Error(), `Invalid isolation: "invalid"`) {
  65. t.Fatal(err)
  66. }
  67. }
  68. // Blank isolation (== default)
  69. if _, _, _, err := callDecodeContainerConfigIsolation(""); err != nil {
  70. t.Fatal("Blank isolation should have succeeded")
  71. }
  72. // Default isolation
  73. if _, _, _, err := callDecodeContainerConfigIsolation("default"); err != nil {
  74. t.Fatal("default isolation should have succeeded")
  75. }
  76. // Process isolation (Valid on Windows only)
  77. if runtime.GOOS == "windows" {
  78. if _, _, _, err := callDecodeContainerConfigIsolation("process"); err != nil {
  79. t.Fatal("process isolation should have succeeded")
  80. }
  81. } else {
  82. if _, _, _, err := callDecodeContainerConfigIsolation("process"); err != nil {
  83. if !strings.Contains(err.Error(), `Invalid isolation: "process"`) {
  84. t.Fatal(err)
  85. }
  86. }
  87. }
  88. // Hyper-V Containers isolation (Valid on Windows only)
  89. if runtime.GOOS == "windows" {
  90. if _, _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
  91. t.Fatal("hyperv isolation should have succeeded")
  92. }
  93. } else {
  94. if _, _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
  95. if !strings.Contains(err.Error(), `Invalid isolation: "hyperv"`) {
  96. t.Fatal(err)
  97. }
  98. }
  99. }
  100. }
  101. // callDecodeContainerConfigIsolation is a utility function to call
  102. // DecodeContainerConfig for validating isolation
  103. func callDecodeContainerConfigIsolation(isolation string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
  104. var (
  105. b []byte
  106. err error
  107. )
  108. w := ContainerConfigWrapper{
  109. Config: &container.Config{},
  110. HostConfig: &container.HostConfig{
  111. NetworkMode: "none",
  112. Isolation: container.Isolation(isolation)},
  113. }
  114. if b, err = json.Marshal(w); err != nil {
  115. return nil, nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
  116. }
  117. return decodeContainerConfig(bytes.NewReader(b))
  118. }
  119. type decodeConfigTestcase struct {
  120. doc string
  121. wrapper ContainerConfigWrapper
  122. expectedErr string
  123. expectedConfig *container.Config
  124. expectedHostConfig *container.HostConfig
  125. goos string
  126. }
  127. func runDecodeContainerConfigTestCase(testcase decodeConfigTestcase) func(t *testing.T) {
  128. return func(t *testing.T) {
  129. raw := marshal(t, testcase.wrapper, testcase.doc)
  130. config, hostConfig, _, err := decodeContainerConfig(bytes.NewReader(raw))
  131. if testcase.expectedErr != "" {
  132. if !assert.Error(t, err) {
  133. return
  134. }
  135. assert.Contains(t, err.Error(), testcase.expectedErr)
  136. return
  137. }
  138. assert.NoError(t, err)
  139. assert.Equal(t, testcase.expectedConfig, config)
  140. assert.Equal(t, testcase.expectedHostConfig, hostConfig)
  141. }
  142. }
  143. func marshal(t *testing.T, w ContainerConfigWrapper, doc string) []byte {
  144. b, err := json.Marshal(w)
  145. require.NoError(t, err, "%s: failed to encode config wrapper", doc)
  146. return b
  147. }
  148. func containerWrapperWithVolume(volume string) ContainerConfigWrapper {
  149. return ContainerConfigWrapper{
  150. Config: &container.Config{
  151. Volumes: map[string]struct{}{
  152. volume: {},
  153. },
  154. },
  155. HostConfig: &container.HostConfig{},
  156. }
  157. }
  158. func containerWrapperWithBind(bind string) ContainerConfigWrapper {
  159. return ContainerConfigWrapper{
  160. Config: &container.Config{
  161. Volumes: map[string]struct{}{},
  162. },
  163. HostConfig: &container.HostConfig{
  164. Binds: []string{bind},
  165. },
  166. }
  167. }