// start the tailer listening to the logFile, and sending the matching // lines to the writer. func (stream *logFileStream) start(logFile io.ReadSeeker, writer io.Writer) { stream.logTailer = tailer.NewTailer(logFile, writer, stream.countedFilterLine) }
// Make sure a template exists that we can clone from. func EnsureCloneTemplate( backingFilesystem string, series string, networkConfig *container.NetworkConfig, authorizedKeys string, aptProxy proxy.Settings, aptMirror string, enablePackageUpdates bool, enableOSUpgrades bool, imageURLGetter container.ImageURLGetter, useAUFS bool, callback func(containerStatus status.Status, info string, data map[string]interface{}) error, ) (_ golxc.Container, err error) { name := fmt.Sprintf("juju-%s-lxc-template", series) defer func() { if err != nil { callback(status.StatusProvisioningError, fmt.Sprintf("Creating container: %v", err), nil) } }() containerDirectory, err := container.NewDirectory(name) if err != nil { return nil, err } lock, err := AcquireTemplateLock(name, "ensure clone exists") if err != nil { return nil, err } defer lock.Unlock() lxcContainer := LxcObjectFactory.New(name) // Early exit if the container has been constructed before. if lxcContainer.IsConstructed() { logger.Infof("template exists, continuing") return lxcContainer, nil } logger.Infof("template does not exist, creating") callback(status.StatusAllocating, "Creating template container; downloading image may take some time", nil) userData, err := containerinit.TemplateUserData( series, authorizedKeys, aptProxy, aptMirror, enablePackageUpdates, enableOSUpgrades, networkConfig, ) if err != nil { logger.Tracef("failed to create template user data for template: %v", err) return nil, err } userDataFilename, err := containerinit.WriteCloudInitFile(containerDirectory, userData) if err != nil { return nil, err } templateParams := []string{ "--debug", // Debug errors in the cloud image "--userdata", userDataFilename, // Our groovey cloud-init "--hostid", name, // Use the container name as the hostid "-r", series, } var caCert []byte if imageURLGetter != nil { arch := arch.HostArch() imageURL, err := imageURLGetter.ImageURL(instance.LXC, series, arch) if err != nil { return nil, errors.Annotatef(err, "cannot determine cached image URL") } templateParams = append(templateParams, "-T", imageURL) caCert = imageURLGetter.CACert() } var extraCreateArgs []string if backingFilesystem == Btrfs { extraCreateArgs = append(extraCreateArgs, "-B", Btrfs) } // Create the container. logger.Tracef("create the template container") err = createContainer( lxcContainer, containerDirectory, networkConfig, extraCreateArgs, templateParams, caCert, ) if err != nil { logger.Errorf("lxc template container creation failed: %v", err) return nil, err } // Make sure that the mount dir has been created. logger.Tracef("make the mount dir for the shared logs") if err := os.MkdirAll(internalLogDir(name), 0755); err != nil { logger.Tracef("failed to create internal /var/log/juju mount dir: %v", err) return nil, err } // Start the lxc container with the appropriate settings for grabbing the // console output and a log file. consoleFile := filepath.Join(containerDirectory, "console.log") lxcContainer.SetLogFile(filepath.Join(containerDirectory, "container.log"), golxc.LogDebug) logger.Tracef("start the container") // We explicitly don't pass through the config file to the container.Start // method as we have passed it through at container creation time. This // is necessary to get the appropriate rootfs reference without explicitly // setting it ourselves. if err = lxcContainer.Start("", consoleFile); err != nil { logger.Errorf("container failed to start: %v", err) return nil, err } logger.Infof("template container started, now wait for it to stop") callback(status.StatusAllocating, "Template container created; waiting for cloud-init to complete", nil) // Perhaps we should wait for it to finish, and the question becomes "how // long do we wait for it to complete?" console, err := os.Open(consoleFile) if err != nil { // can't listen return nil, err } tailWriter := &logTail{tick: time.Now()} consoleTailer := tailer.NewTailer(console, tailWriter, nil) defer consoleTailer.Stop() // We should wait maybe 1 minute between output? // if no output check to see if stopped // If we have no output and still running, something has probably gone wrong for lxcContainer.IsRunning() { if tailWriter.lastTick().Before(time.Now().Add(-TemplateStopTimeout)) { logger.Infof("not heard anything from the template log for five minutes") callback(status.StatusProvisioningError, "Container creation failed: template container has not stopped", nil) return nil, fmt.Errorf("template container %q did not stop", name) } time.Sleep(time.Second) } return lxcContainer, nil }
// Make sure a template exists that we can clone from. func EnsureCloneTemplate( backingFilesystem string, series string, network *container.NetworkConfig, authorizedKeys string, aptProxy proxy.Settings, ) (golxc.Container, error) { name := fmt.Sprintf("juju-%s-template", series) containerDirectory, err := container.NewDirectory(name) if err != nil { return nil, err } lock, err := AcquireTemplateLock(name, "ensure clone exists") if err != nil { return nil, err } defer lock.Unlock() lxcContainer := LxcObjectFactory.New(name) // Early exit if the container has been constructed before. if lxcContainer.IsConstructed() { logger.Infof("template exists, continuing") return lxcContainer, nil } logger.Infof("template does not exist, creating") userData, err := templateUserData(series, authorizedKeys, aptProxy) if err != nil { logger.Tracef("failed to create template user data for template: %v", err) return nil, err } userDataFilename, err := container.WriteCloudInitFile(containerDirectory, userData) if err != nil { return nil, err } configFile, err := writeLxcConfig(network, containerDirectory) if err != nil { logger.Errorf("failed to write config file: %v", err) return nil, err } templateParams := []string{ "--debug", // Debug errors in the cloud image "--userdata", userDataFilename, // Our groovey cloud-init "--hostid", name, // Use the container name as the hostid "-r", series, } var extraCreateArgs []string if backingFilesystem == Btrfs { extraCreateArgs = append(extraCreateArgs, "-B", Btrfs) } // Create the container. logger.Tracef("create the container") if err := lxcContainer.Create(configFile, defaultTemplate, extraCreateArgs, templateParams); err != nil { logger.Errorf("lxc container creation failed: %v", err) return nil, err } // Make sure that the mount dir has been created. logger.Tracef("make the mount dir for the shared logs") if err := os.MkdirAll(internalLogDir(name), 0755); err != nil { logger.Tracef("failed to create internal /var/log/juju mount dir: %v", err) return nil, err } // Start the lxc container with the appropriate settings for grabbing the // console output and a log file. consoleFile := filepath.Join(containerDirectory, "console.log") lxcContainer.SetLogFile(filepath.Join(containerDirectory, "container.log"), golxc.LogDebug) logger.Tracef("start the container") // We explicitly don't pass through the config file to the container.Start // method as we have passed it through at container creation time. This // is necessary to get the appropriate rootfs reference without explicitly // setting it ourselves. if err = lxcContainer.Start("", consoleFile); err != nil { logger.Errorf("container failed to start: %v", err) return nil, err } logger.Infof("template container started, now wait for it to stop") // Perhaps we should wait for it to finish, and the question becomes "how // long do we wait for it to complete?" console, err := os.Open(consoleFile) if err != nil { // can't listen return nil, err } tailWriter := &logTail{tick: time.Now()} consoleTailer := tailer.NewTailer(console, tailWriter, nil) defer consoleTailer.Stop() // We should wait maybe 1 minute between output? // if no output check to see if stopped // If we have no output and still running, something has probably gone wrong for lxcContainer.IsRunning() { if tailWriter.lastTick().Before(time.Now().Add(-TemplateStopTimeout)) { logger.Infof("not heard anything from the template log for five minutes") return nil, fmt.Errorf("template container %q did not stop", name) } time.Sleep(time.Second) } return lxcContainer, nil }