builder.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package docker
  2. import (
  3. "fmt"
  4. "os"
  5. "path"
  6. "time"
  7. )
  8. var defaultDns = []string{"8.8.8.8", "8.8.4.4"}
  9. type Builder struct {
  10. runtime *Runtime
  11. repositories *TagStore
  12. graph *Graph
  13. config *Config
  14. image *Image
  15. }
  16. func NewBuilder(runtime *Runtime) *Builder {
  17. return &Builder{
  18. runtime: runtime,
  19. graph: runtime.graph,
  20. repositories: runtime.repositories,
  21. }
  22. }
  23. func (builder *Builder) Create(config *Config) (*Container, error) {
  24. // Lookup image
  25. img, err := builder.repositories.LookupImage(config.Image)
  26. if err != nil {
  27. return nil, err
  28. }
  29. if img.Config != nil {
  30. MergeConfig(config, img.Config)
  31. }
  32. if config.Cmd == nil || len(config.Cmd) == 0 {
  33. return nil, fmt.Errorf("No command specified")
  34. }
  35. // Generate id
  36. id := GenerateID()
  37. // Generate default hostname
  38. // FIXME: the lxc template no longer needs to set a default hostname
  39. if config.Hostname == "" {
  40. config.Hostname = id[:12]
  41. }
  42. container := &Container{
  43. // FIXME: we should generate the ID here instead of receiving it as an argument
  44. ID: id,
  45. Created: time.Now(),
  46. Path: config.Cmd[0],
  47. Args: config.Cmd[1:], //FIXME: de-duplicate from config
  48. Config: config,
  49. Image: img.ID, // Always use the resolved image id
  50. NetworkSettings: &NetworkSettings{},
  51. // FIXME: do we need to store this in the container?
  52. SysInitPath: sysInitPath,
  53. }
  54. container.root = builder.runtime.containerRoot(container.ID)
  55. // Step 1: create the container directory.
  56. // This doubles as a barrier to avoid race conditions.
  57. if err := os.Mkdir(container.root, 0700); err != nil {
  58. return nil, err
  59. }
  60. // If custom dns exists, then create a resolv.conf for the container
  61. if len(config.Dns) > 0 || len(builder.runtime.Dns) > 0 {
  62. var dns []string
  63. if len(config.Dns) > 0 {
  64. dns = config.Dns
  65. } else {
  66. dns = builder.runtime.Dns
  67. }
  68. container.ResolvConfPath = path.Join(container.root, "resolv.conf")
  69. f, err := os.Create(container.ResolvConfPath)
  70. if err != nil {
  71. return nil, err
  72. }
  73. defer f.Close()
  74. for _, dns := range dns {
  75. if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
  76. return nil, err
  77. }
  78. }
  79. } else {
  80. container.ResolvConfPath = "/etc/resolv.conf"
  81. }
  82. // Step 2: save the container json
  83. if err := container.ToDisk(); err != nil {
  84. return nil, err
  85. }
  86. // Step 3: register the container
  87. if err := builder.runtime.Register(container); err != nil {
  88. return nil, err
  89. }
  90. return container, nil
  91. }
  92. // Commit creates a new filesystem image from the current state of a container.
  93. // The image can optionally be tagged into a repository
  94. func (builder *Builder) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) {
  95. // FIXME: freeze the container before copying it to avoid data corruption?
  96. // FIXME: this shouldn't be in commands.
  97. rwTar, err := container.ExportRw()
  98. if err != nil {
  99. return nil, err
  100. }
  101. // Create a new image from the container's base layers + a new layer from container changes
  102. img, err := builder.graph.Create(rwTar, container, comment, author, config)
  103. if err != nil {
  104. return nil, err
  105. }
  106. // Register the image if needed
  107. if repository != "" {
  108. if err := builder.repositories.Set(repository, tag, img.ID, true); err != nil {
  109. return img, err
  110. }
  111. }
  112. return img, nil
  113. }