You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

117 lines
2.6 KiB

  1. package docker
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "gerrit.wikimedia.org/r/blubber/build"
  7. )
  8. // NewInstruction takes a general internal build.Instruction and returns
  9. // a corresponding compilable Docker specific instruction. The given internal
  10. // instruction is partially compiled at this point by calling Compile() which
  11. // applies its own logic for escaping arguments, etc.
  12. //
  13. func NewInstruction(bi build.Instruction) (Instruction, error) {
  14. i := instruction{arguments: bi.Compile()}
  15. switch bi.(type) {
  16. case build.Run, build.RunAll:
  17. i.name = "RUN"
  18. case build.Copy, build.CopyAs, build.CopyFrom:
  19. i.name = "COPY"
  20. i.array = true
  21. switch bi.(type) {
  22. case build.CopyAs:
  23. switch bi.(build.CopyAs).Instruction.(type) {
  24. case build.Copy:
  25. i.flags = []string{"chown"}
  26. case build.CopyFrom:
  27. i.flags = []string{"chown", "from"}
  28. }
  29. case build.CopyFrom:
  30. i.flags = []string{"from"}
  31. }
  32. case build.EntryPoint:
  33. i.name = "ENTRYPOINT"
  34. i.array = true
  35. case build.Env:
  36. i.name = "ENV"
  37. i.separator = " "
  38. case build.Label:
  39. i.name = "LABEL"
  40. i.separator = " "
  41. case build.User:
  42. i.name = "USER"
  43. case build.WorkingDirectory:
  44. i.name = "WORKDIR"
  45. }
  46. if i.name == "" {
  47. return nil, errors.New("Unable to create Instruction")
  48. }
  49. return i, nil
  50. }
  51. // Instruction defines an interface for instruction compilation.
  52. //
  53. type Instruction interface {
  54. Compile() string
  55. }
  56. type instruction struct {
  57. name string // name (e.g. "RUN")
  58. flags []string // flags (e.g. "chown")
  59. arguments []string // quoted arguments
  60. separator string // argument separator
  61. array bool // format arguments as array (enforces ", " separator)
  62. }
  63. // Compile returns a valid Dockerfile line for the instruction.
  64. //
  65. // Output is in the format "<name> <flags> <arguments>", e.g.
  66. // "COPY --chown=123:223 ["foo", "bar"]" and flag values are taken from the
  67. // beginning of the arguments slice.
  68. //
  69. func (ins instruction) Compile() string {
  70. format := ins.name + " "
  71. numFlags := len(ins.flags)
  72. args := make([]interface{}, numFlags+1)
  73. for i, option := range ins.flags {
  74. format += "--" + option + "=%s "
  75. args[i] = ins.arguments[i]
  76. }
  77. separator := ins.separator
  78. if ins.array {
  79. separator = ", "
  80. format += "[%s]"
  81. } else {
  82. format += "%s"
  83. }
  84. format += "\n"
  85. args[numFlags] = join(ins.arguments[numFlags:], separator)
  86. return fmt.Sprintf(format, args...)
  87. }
  88. func join(arguments []string, delimiter string) string {
  89. return removeNewlines(strings.Join(arguments, delimiter))
  90. }
  91. func removeNewlines(instructions string) string {
  92. out := strings.Replace(instructions, "\n", "\\n", -1)
  93. return out
  94. }