github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/agent/mongo/mongo.go (about)

     1  package mongo
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"path"
     8  	"path/filepath"
     9  
    10  	"github.com/juju/loggo"
    11  
    12  	"launchpad.net/juju-core/upstart"
    13  	"launchpad.net/juju-core/utils"
    14  )
    15  
    16  const (
    17  	maxFiles = 65000
    18  	maxProcs = 20000
    19  )
    20  
    21  var (
    22  	logger = loggo.GetLogger("juju.agent.mongo")
    23  
    24  	oldMongoServiceName = "juju-db"
    25  
    26  	// JujuMongodPath holds the default path to the juju-specific mongod.
    27  	JujuMongodPath = "/usr/lib/juju/bin/mongod"
    28  )
    29  
    30  // MongoPath returns the executable path to be used to run mongod on this
    31  // machine. If the juju-bundled version of mongo exists, it will return that
    32  // path, otherwise it will return the command to run mongod from the path.
    33  func MongodPath() (string, error) {
    34  	if _, err := os.Stat(JujuMongodPath); err == nil {
    35  		return JujuMongodPath, nil
    36  	}
    37  
    38  	path, err := exec.LookPath("mongod")
    39  	if err != nil {
    40  		return "", err
    41  	}
    42  	return path, nil
    43  }
    44  
    45  // EnsureMongoServer ensures that the correct mongo upstart script is installed
    46  // and running.
    47  //
    48  // This method will remove old versions of the mongo upstart script as necessary
    49  // before installing the new version.
    50  func EnsureMongoServer(dir string, port int) error {
    51  	name := makeServiceName(mongoScriptVersion)
    52  	service, err := MongoUpstartService(name, dir, port)
    53  	if err != nil {
    54  		return err
    55  	}
    56  	if service.Installed() {
    57  		return nil
    58  	}
    59  
    60  	if err := removeOldMongoServices(mongoScriptVersion); err != nil {
    61  		return err
    62  	}
    63  
    64  	if err := makeJournalDirs(dir); err != nil {
    65  		return err
    66  	}
    67  
    68  	if err := service.Install(); err != nil {
    69  		return fmt.Errorf("failed to install mongo service %q: %v", service.Name, err)
    70  	}
    71  	return service.Start()
    72  }
    73  
    74  func makeJournalDirs(dir string) error {
    75  	journalDir := path.Join(dir, "journal")
    76  
    77  	if err := os.MkdirAll(journalDir, 0700); err != nil {
    78  		logger.Errorf("failed to make mongo journal dir %s: %v", journalDir, err)
    79  		return err
    80  	}
    81  
    82  	// manually create the prealloc files, since otherwise they get created as 100M files.
    83  	zeroes := make([]byte, 64*1024) // should be enough for anyone
    84  	for x := 0; x < 3; x++ {
    85  		name := fmt.Sprintf("prealloc.%d", x)
    86  		filename := filepath.Join(journalDir, name)
    87  		f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700)
    88  		if err != nil {
    89  			return fmt.Errorf("failed to open mongo prealloc file %q: %v", filename, err)
    90  		}
    91  		defer f.Close()
    92  		for total := 0; total < 1024*1024; {
    93  			n, err := f.Write(zeroes)
    94  			if err != nil {
    95  				return fmt.Errorf("failed to write to mongo prealloc file %q: %v", filename, err)
    96  			}
    97  			total += n
    98  		}
    99  	}
   100  	return nil
   101  }
   102  
   103  // removeOldMongoServices looks for any old juju mongo upstart scripts and
   104  // removes them.
   105  func removeOldMongoServices(curVersion int) error {
   106  	old := upstart.NewService(oldMongoServiceName)
   107  	if err := old.StopAndRemove(); err != nil {
   108  		logger.Errorf("Failed to remove old mongo upstart service %q: %v", old.Name, err)
   109  		return err
   110  	}
   111  
   112  	// the new formatting for the script name started at version 2
   113  	for x := 2; x < curVersion; x++ {
   114  		old := upstart.NewService(makeServiceName(x))
   115  		if err := old.StopAndRemove(); err != nil {
   116  			logger.Errorf("Failed to remove old mongo upstart service %q: %v", old.Name, err)
   117  			return err
   118  		}
   119  	}
   120  	return nil
   121  }
   122  
   123  func makeServiceName(version int) string {
   124  	return fmt.Sprintf("juju-db-v%d", version)
   125  }
   126  
   127  // mongoScriptVersion keeps track of changes to the mongo upstart script.
   128  // Update this version when you update the script that gets installed from
   129  // MongoUpstartService.
   130  const mongoScriptVersion = 2
   131  
   132  // MongoUpstartService returns the upstart config for the mongo state service.
   133  //
   134  // This method assumes there is a server.pem keyfile in dataDir.
   135  func MongoUpstartService(name, dataDir string, port int) (*upstart.Conf, error) {
   136  
   137  	keyFile := path.Join(dataDir, "server.pem")
   138  	svc := upstart.NewService(name)
   139  
   140  	dbDir := path.Join(dataDir, "db")
   141  
   142  	conf := &upstart.Conf{
   143  		Service: *svc,
   144  		Desc:    "juju state database",
   145  		Limit: map[string]string{
   146  			"nofile": fmt.Sprintf("%d %d", maxFiles, maxFiles),
   147  			"nproc":  fmt.Sprintf("%d %d", maxProcs, maxProcs),
   148  		},
   149  		Cmd: "/usr/bin/mongod" +
   150  			" --auth" +
   151  			" --dbpath=" + dbDir +
   152  			" --sslOnNormalPorts" +
   153  			" --sslPEMKeyFile " + utils.ShQuote(keyFile) +
   154  			" --sslPEMKeyPassword ignored" +
   155  			" --bind_ip 0.0.0.0" +
   156  			" --port " + fmt.Sprint(port) +
   157  			" --noprealloc" +
   158  			" --syslog" +
   159  			" --smallfiles",
   160  		// TODO(Nate): uncomment when we commit HA stuff
   161  		// +
   162  		//	" --replSet juju",
   163  	}
   164  	return conf, nil
   165  }