github.com/emc-advanced-dev/unik@v0.0.0-20190717152701-a58d3e8e33b7/pkg/daemon/daemon.go (about)

     1  package daemon
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"os"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	"path/filepath"
    16  
    17  	"github.com/docker/docker/pkg/ioutils"
    18  	"github.com/emc-advanced-dev/pkg/errors"
    19  	"github.com/go-martini/martini"
    20  	"github.com/layer-x/layerx-commons/lxmartini"
    21  	"github.com/sirupsen/logrus"
    22  	"github.com/solo-io/unik/pkg/compilers"
    23  	firecrackercompiler "github.com/solo-io/unik/pkg/compilers/firecracker"
    24  	"github.com/solo-io/unik/pkg/compilers/includeos"
    25  	"github.com/solo-io/unik/pkg/compilers/mirage"
    26  	"github.com/solo-io/unik/pkg/compilers/osv"
    27  	"github.com/solo-io/unik/pkg/compilers/rump"
    28  
    29  	"github.com/solo-io/unik/pkg/config"
    30  	unikos "github.com/solo-io/unik/pkg/os"
    31  	"github.com/solo-io/unik/pkg/providers"
    32  	"github.com/solo-io/unik/pkg/providers/aws"
    33  	firecrackerprovider "github.com/solo-io/unik/pkg/providers/firecracker"
    34  	"github.com/solo-io/unik/pkg/providers/gcloud"
    35  	"github.com/solo-io/unik/pkg/providers/openstack"
    36  	"github.com/solo-io/unik/pkg/providers/photon"
    37  	"github.com/solo-io/unik/pkg/providers/qemu"
    38  	"github.com/solo-io/unik/pkg/providers/ukvm"
    39  	"github.com/solo-io/unik/pkg/providers/virtualbox"
    40  	"github.com/solo-io/unik/pkg/providers/vsphere"
    41  	"github.com/solo-io/unik/pkg/providers/xen"
    42  
    43  	"github.com/solo-io/unik/pkg/state"
    44  	"github.com/solo-io/unik/pkg/types"
    45  	"github.com/solo-io/unik/pkg/util"
    46  )
    47  
    48  type UnikDaemon struct {
    49  	server    *martini.ClassicMartini
    50  	providers providers.Providers `json:"providers"`
    51  	compilers map[compilers.CompilerType]compilers.Compiler
    52  }
    53  
    54  const (
    55  	//available providers
    56  	aws_provider         = "aws"
    57  	vsphere_provider     = "vsphere"
    58  	virtualbox_provider  = "virtualbox"
    59  	qemu_provider        = "qemu"
    60  	photon_provider      = "photon"
    61  	xen_provider         = "xen"
    62  	ukvm_provider        = "ukvm"
    63  	gcloud_provider      = "gcloud"
    64  	openstack_provider   = "openstack"
    65  	firecracker_provider = "firecracker"
    66  )
    67  
    68  func NewUnikDaemon(config config.DaemonConfig) (*UnikDaemon, error) {
    69  	if err := util.InitContainers(); err != nil {
    70  		return nil, errors.New("initializing containers", err)
    71  	}
    72  
    73  	//set up tmpdrir
    74  	tmpDir := filepath.Join(os.Getenv("HOME"), ".unik", "tmp")
    75  	os.Setenv("TMPDIR", tmpDir)
    76  	os.MkdirAll(tmpDir, 0755)
    77  
    78  	_providers := make(providers.Providers)
    79  	_compilers := make(map[compilers.CompilerType]compilers.Compiler)
    80  
    81  	for _, awsConfig := range config.Providers.Aws {
    82  		logrus.Infof("Bootstrapping provider %s with config %v", aws_provider, awsConfig)
    83  		p := aws.NewAwsProvier(awsConfig)
    84  		s, err := state.BasicStateFromFile(aws.AwsStateFile())
    85  		if err != nil {
    86  			logrus.WithError(err).Warnf("failed to read aws state file at %s, creating blank aws state", aws.AwsStateFile())
    87  			s = state.NewBasicState(aws.AwsStateFile())
    88  		}
    89  		p = p.WithState(s)
    90  		_providers[aws_provider] = p
    91  		break
    92  	}
    93  	for _, vsphereConfig := range config.Providers.Vsphere {
    94  		logrus.Infof("Bootstrapping provider %s with config %v", vsphere_provider, vsphereConfig)
    95  		p, err := vsphere.NewVsphereProvier(vsphereConfig)
    96  		if err != nil {
    97  			return nil, errors.New("initializing vsphere provider", err)
    98  		}
    99  		s, err := state.BasicStateFromFile(vsphere.VsphereStateFile())
   100  		if err != nil {
   101  			logrus.WithError(err).Warnf("failed to read vsphere state file at %s, creating blank vsphere state", vsphere.VsphereStateFile())
   102  			s = state.NewBasicState(vsphere.VsphereStateFile())
   103  		}
   104  		p = p.WithState(s)
   105  		_providers[vsphere_provider] = p
   106  		break
   107  	}
   108  	for _, virtualboxConfig := range config.Providers.Virtualbox {
   109  		logrus.Infof("Bootstrapping provider %s with config %v", virtualbox_provider, virtualboxConfig)
   110  		p, err := virtualbox.NewVirtualboxProvider(virtualboxConfig)
   111  		if err != nil {
   112  			return nil, errors.New("initializing virtualbox provider", err)
   113  		}
   114  		s, err := state.BasicStateFromFile(virtualbox.VirtualboxStateFile())
   115  		if err != nil {
   116  			logrus.WithError(err).Warnf("failed to read virtualbox state file at %s, creating blank virtualbox state", virtualbox.VirtualboxStateFile())
   117  			s = state.NewBasicState(virtualbox.VirtualboxStateFile())
   118  		}
   119  		p = p.WithState(s)
   120  		_providers[virtualbox_provider] = p
   121  		break
   122  	}
   123  
   124  	for _, qemuConfig := range config.Providers.Qemu {
   125  		logrus.Infof("Bootstrapping provider %s with config %v", qemu_provider, qemuConfig)
   126  		p, err := qemu.NewQemuProvider(qemuConfig)
   127  		if err != nil {
   128  			return nil, errors.New("initializing qemu provider", err)
   129  		}
   130  		s, err := state.BasicStateFromFile(qemu.QemuStateFile())
   131  		if err != nil {
   132  			logrus.WithError(err).Warnf("failed to read qemu state file at %s, creating blank qemu state", qemu.QemuStateFile())
   133  			s = state.NewBasicState(qemu.QemuStateFile())
   134  		}
   135  		p = p.WithState(s)
   136  		_providers[qemu_provider] = p
   137  		break
   138  	}
   139  
   140  	for _, photonConfig := range config.Providers.Photon {
   141  		logrus.Infof("Bootstrapping provider %s with config %v", photon_provider, photonConfig)
   142  		p, err := photon.NewPhotonProvider(photonConfig)
   143  		if err != nil {
   144  			return nil, errors.New("initializing photon provider", err)
   145  		}
   146  		s, err := state.BasicStateFromFile(photon.PhotonStateFile())
   147  		if err != nil {
   148  			logrus.WithError(err).Warnf("failed to read photon state file at %s, creating blank photon state", photon.PhotonStateFile())
   149  			s = state.NewBasicState(photon.PhotonStateFile())
   150  		}
   151  		p = p.WithState(s)
   152  		_providers[photon_provider] = p
   153  		break
   154  	}
   155  
   156  	for _, openstackConfig := range config.Providers.Openstack {
   157  		openstack.MergeConfWithEnv(&openstackConfig)
   158  
   159  		// Mask password prior logging to console.
   160  		orig_pass := openstackConfig.Password
   161  		openstackConfig.Password = "<password>"
   162  		logrus.Infof("Bootstrapping provider %s with config %v", openstack_provider, openstackConfig)
   163  		openstackConfig.Password = orig_pass
   164  
   165  		p, err := openstack.NewOpenstackProvider(openstackConfig)
   166  		if err != nil {
   167  			return nil, errors.New("initializing openstack provider", err)
   168  		}
   169  		s, err := state.BasicStateFromFile(openstack.OpenstackStateFile())
   170  		if err != nil {
   171  			logrus.WithError(err).Warnf("failed to read openstack state file at %s, creating blank openstack state", openstack.OpenstackStateFile())
   172  			s = state.NewBasicState(openstack.OpenstackStateFile())
   173  		}
   174  		p = p.WithState(s)
   175  		_providers[openstack_provider] = p
   176  		break
   177  	}
   178  
   179  	for _, xenConfig := range config.Providers.Xen {
   180  		logrus.Infof("Bootstrapping provider %s with config %v", xen_provider, xenConfig)
   181  		p, err := xen.NewXenProvider(xenConfig)
   182  		if err != nil {
   183  			return nil, errors.New("initializing xen provider", err)
   184  		}
   185  		s, err := state.BasicStateFromFile(xen.XenStateFile())
   186  		if err != nil {
   187  			logrus.WithError(err).Warnf("failed to read xen state file at %s, creating blank state", xen.XenStateFile())
   188  			s = state.NewBasicState(xen.XenStateFile())
   189  		}
   190  		p = p.WithState(s)
   191  		_providers[xen_provider] = p
   192  		break
   193  	}
   194  
   195  	for _, ukvmConfig := range config.Providers.Ukvm {
   196  		logrus.Infof("Bootstrapping provider %s with config %v", ukvm_provider, ukvmConfig)
   197  		p, err := ukvm.NewUkvmProvider(ukvmConfig)
   198  		if err != nil {
   199  			return nil, errors.New("initializing ukvm provider", err)
   200  		}
   201  		s, err := state.BasicStateFromFile(ukvm.UkvmStateFile())
   202  		if err != nil {
   203  			logrus.WithError(err).Warnf("failed to read ukvm state file at %s, creating blank state", ukvm.UkvmStateFile())
   204  			s = state.NewBasicState(ukvm.UkvmStateFile())
   205  		}
   206  		p = p.WithState(s)
   207  		_providers[ukvm_provider] = p
   208  		break
   209  	}
   210  
   211  	for _, gcloudConfig := range config.Providers.Gcloud {
   212  		logrus.Infof("Bootstrapping provider %s with config %v", gcloud_provider, gcloudConfig)
   213  		p, err := gcloud.NewGcloudProvier(gcloudConfig)
   214  		if err != nil {
   215  			return nil, errors.New("initializing gcloud provider", err)
   216  		}
   217  		s, err := state.BasicStateFromFile(gcloud.GcloudStateFile())
   218  		if err != nil {
   219  			logrus.WithError(err).Warnf("failed to read gcloud state file at %s, creating blank state", gcloud.GcloudStateFile())
   220  			s = state.NewBasicState(gcloud.GcloudStateFile())
   221  		}
   222  		p = p.WithState(s)
   223  		_providers[gcloud_provider] = p
   224  		break
   225  	}
   226  
   227  	for _, firecrackerConfig := range config.Providers.Firecracker {
   228  		logrus.Infof("Bootstrapping provider %s with config %v", firecracker_provider, firecrackerConfig)
   229  		p, err := firecrackerprovider.NewProvider(firecrackerConfig)
   230  		if err != nil {
   231  			return nil, errors.New("initializing firecracker provider", err)
   232  		}
   233  		s, err := state.BasicStateFromFile(firecrackerprovider.FirecrackerStateFile())
   234  		if err != nil {
   235  			logrus.WithError(err).Warnf("failed to read firecracker state file at %s, creating blank state", firecrackerprovider.FirecrackerStateFile())
   236  			s = state.NewBasicState(firecrackerprovider.FirecrackerStateFile())
   237  		}
   238  		p = p.WithState(s)
   239  		_providers[firecracker_provider] = p
   240  		break
   241  	}
   242  
   243  	//rump-go
   244  	_compilers[compilers.RUMP_GO_PHOTON] = &rump.RumpGoCompiler{
   245  		RumCompilerBase: rump.RumCompilerBase{
   246  			DockerImage: "compilers-rump-go-hw",
   247  			CreateImage: rump.CreateImageVmware,
   248  		},
   249  		BootstrapType: rump.BootstrapTypeUDP,
   250  	}
   251  	rumpGoXenCompiler := &rump.RumpGoCompiler{
   252  		RumCompilerBase: rump.RumCompilerBase{
   253  			DockerImage: "compilers-rump-go-xen",
   254  			CreateImage: rump.CreateImageXen,
   255  		},
   256  		BootstrapType: rump.BootstrapTypeUDP,
   257  	}
   258  	_compilers[compilers.RUMP_GO_XEN] = rumpGoXenCompiler
   259  	_compilers[compilers.RUMP_GO_AWS] = rumpGoXenCompiler
   260  	_compilers[compilers.RUMP_GO_VSPHERE] = &rump.RumpGoCompiler{
   261  		RumCompilerBase: rump.RumCompilerBase{
   262  
   263  			DockerImage: "compilers-rump-go-hw",
   264  			CreateImage: rump.CreateImageVmware,
   265  		},
   266  		BootstrapType: rump.BootstrapTypeUDP,
   267  	}
   268  	_compilers[compilers.RUMP_GO_VIRTUALBOX] = &rump.RumpGoCompiler{
   269  		RumCompilerBase: rump.RumCompilerBase{
   270  
   271  			DockerImage: "compilers-rump-go-hw",
   272  			CreateImage: rump.CreateImageVirtualBox,
   273  		},
   274  		BootstrapType: rump.BootstrapTypeUDP,
   275  	}
   276  	_compilers[compilers.RUMP_GO_QEMU] = &rump.RumpGoCompiler{
   277  		RumCompilerBase: rump.RumCompilerBase{
   278  			DockerImage: "compilers-rump-go-hw",
   279  			CreateImage: rump.CreateImageQemu,
   280  		},
   281  		BootstrapType: rump.BootstrapTypeNoStub,
   282  	}
   283  	_compilers[compilers.RUMP_GO_OPENSTACK] = &rump.RumpGoCompiler{
   284  		RumCompilerBase: rump.RumCompilerBase{
   285  			DockerImage: "compilers-rump-go-hw",
   286  			CreateImage: rump.CreateImageQemu,
   287  		},
   288  		BootstrapType: rump.BootstrapTypeNoStub,
   289  	}
   290  	_compilers[compilers.RUMP_GO_GCLOUD] = &rump.RumpGoCompiler{
   291  		RumCompilerBase: rump.RumCompilerBase{
   292  			DockerImage: "compilers-rump-go-hw",
   293  			CreateImage: rump.CreateImageGCloud,
   294  		},
   295  		BootstrapType: rump.BootstrapTypeGCLOUD,
   296  	}
   297  
   298  	//includeos-cpp
   299  	_compilers[compilers.INCLUDEOS_CPP_QEMU] = &includeos.IncludeosQemuCompiler{}
   300  	_compilers[compilers.INCLUDEOS_CPP_VIRTUALBOX] = &includeos.IncludeosVirtualboxCompiler{}
   301  	_compilers[compilers.INCLUDEOS_CPP_OPENSTACK] = &includeos.IncludeosQemuCompiler{}
   302  
   303  	//rump nodejs
   304  	_compilers[compilers.RUMP_NODEJS_XEN] = &rump.RumpScriptCompiler{
   305  		RumCompilerBase: rump.RumCompilerBase{
   306  			DockerImage: "compilers-rump-nodejs-xen",
   307  			CreateImage: rump.CreateImageXen,
   308  		},
   309  		BootstrapType: rump.BootstrapTypeUDP,
   310  		RunScriptArgs: "/bootpart/node-wrapper.js",
   311  	}
   312  	_compilers[compilers.RUMP_NODEJS_AWS] = &rump.RumpScriptCompiler{
   313  		RumCompilerBase: rump.RumCompilerBase{
   314  			DockerImage: "compilers-rump-nodejs-xen",
   315  			CreateImage: rump.CreateImageXen,
   316  		},
   317  		BootstrapType: rump.BootstrapTypeEC2,
   318  		RunScriptArgs: "/bootpart/node-wrapper.js",
   319  	}
   320  	_compilers[compilers.RUMP_NODEJS_VIRTUALBOX] = &rump.RumpScriptCompiler{
   321  		RumCompilerBase: rump.RumCompilerBase{
   322  			DockerImage: "compilers-rump-nodejs-hw",
   323  			CreateImage: rump.CreateImageVirtualBox,
   324  		},
   325  		BootstrapType: rump.BootstrapTypeUDP,
   326  		RunScriptArgs: "/bootpart/node-wrapper.js",
   327  	}
   328  	_compilers[compilers.RUMP_NODEJS_VSPHERE] = &rump.RumpScriptCompiler{
   329  		RumCompilerBase: rump.RumCompilerBase{
   330  			DockerImage: "compilers-rump-nodejs-hw",
   331  			CreateImage: rump.CreateImageVmware,
   332  		},
   333  		BootstrapType: rump.BootstrapTypeUDP,
   334  		RunScriptArgs: "/bootpart/node-wrapper.js",
   335  	}
   336  	_compilers[compilers.RUMP_NODEJS_QEMU] = &rump.RumpScriptCompiler{
   337  		RumCompilerBase: rump.RumCompilerBase{
   338  			DockerImage: "compilers-rump-nodejs-hw-no-stub",
   339  			CreateImage: rump.CreateImageQemu,
   340  		},
   341  		RunScriptArgs: "/bootpart/node-wrapper.js",
   342  	}
   343  	_compilers[compilers.RUMP_NODEJS_OPENSTACK] = &rump.RumpScriptCompiler{
   344  		RumCompilerBase: rump.RumCompilerBase{
   345  			DockerImage: "compilers-rump-nodejs-hw-no-stub",
   346  			CreateImage: rump.CreateImageQemu,
   347  		},
   348  		RunScriptArgs: "/bootpart/node-wrapper.js",
   349  	}
   350  
   351  	//mirage ocaml
   352  	_compilers[compilers.MIRAGE_OCAML_XEN] = &mirage.MirageCompiler{Type: mirage.XenType}
   353  	_compilers[compilers.MIRAGE_OCAML_UKVM] = &mirage.MirageCompiler{Type: mirage.UKVMType}
   354  	// _compilers[compilers.MIRAGE_OCAML_QEMU] = &mirage.MirageCompiler{Type: mirage.VirtioType}
   355  
   356  	//rump python
   357  	_compilers[compilers.RUMP_PYTHON_XEN] = rump.NewRumpPythonCompiler("compilers-rump-python3-xen", rump.CreateImageXenAddStub, rump.BootstrapTypeUDP)
   358  	_compilers[compilers.RUMP_PYTHON_AWS] = rump.NewRumpPythonCompiler("compilers-rump-python3-xen", rump.CreateImageXenAddStub, rump.BootstrapTypeEC2)
   359  	_compilers[compilers.RUMP_PYTHON_VIRTUALBOX] = rump.NewRumpPythonCompiler("compilers-rump-python3-hw", rump.CreateImageVirtualBoxAddStub, rump.BootstrapTypeUDP)
   360  	_compilers[compilers.RUMP_PYTHON_VSPHERE] = rump.NewRumpPythonCompiler("compilers-rump-python3-hw", rump.CreateImageVmwareAddStub, rump.BootstrapTypeUDP)
   361  	_compilers[compilers.RUMP_PYTHON_QEMU] = rump.NewRumpPythonCompiler("compilers-rump-python3-hw-no-stub", rump.CreateImageQemu, rump.BootstrapTypeNoStub)
   362  	_compilers[compilers.RUMP_PYTHON_OPENSTACK] = rump.NewRumpPythonCompiler("compilers-rump-python3-hw-no-stub", rump.CreateImageQemu, rump.BootstrapTypeNoStub)
   363  
   364  	//rump java
   365  	_compilers[compilers.RUMP_JAVA_XEN] = rump.NewRumpJavaCompiler("compilers-rump-java-xen", rump.CreateImageXen, rump.BootstrapTypeUDP)
   366  	_compilers[compilers.RUMP_JAVA_AWS] = rump.NewRumpJavaCompiler("compilers-rump-java-xen", rump.CreateImageXen, rump.BootstrapTypeEC2)
   367  	_compilers[compilers.RUMP_JAVA_VIRTUALBOX] = rump.NewRumpJavaCompiler("compilers-rump-java-hw", rump.CreateImageVirtualBox, rump.BootstrapTypeUDP)
   368  	_compilers[compilers.RUMP_JAVA_VSPHERE] = rump.NewRumpJavaCompiler("compilers-rump-java-hw", rump.CreateImageVmware, rump.BootstrapTypeUDP)
   369  	_compilers[compilers.RUMP_JAVA_QEMU] = rump.NewRumpJavaCompiler("compilers-rump-java-hw", rump.CreateImageQemu, rump.BootstrapTypeNoStub)
   370  	_compilers[compilers.RUMP_JAVA_OPENSTACK] = rump.NewRumpJavaCompiler("compilers-rump-java-hw", rump.CreateImageQemu, rump.BootstrapTypeNoStub)
   371  
   372  	//rump c
   373  	_compilers[compilers.RUMP_C_XEN] = rump.NewRumpCCompiler("compilers-rump-c-xen", rump.CreateImageXenAddStub)
   374  	_compilers[compilers.RUMP_C_AWS] = rump.NewRumpCCompiler("compilers-rump-c-xen", rump.CreateImageXenAddStub)
   375  	_compilers[compilers.RUMP_C_VIRTUALBOX] = rump.NewRumpCCompiler("compilers-rump-c-hw", rump.CreateImageVirtualBoxAddStub)
   376  	_compilers[compilers.RUMP_C_VSPHERE] = rump.NewRumpCCompiler("compilers-rump-c-hw", rump.CreateImageVmwareAddStub)
   377  	_compilers[compilers.RUMP_C_QEMU] = rump.NewRumpCCompiler("compilers-rump-c-hw", rump.CreateImageQemu)
   378  	_compilers[compilers.RUMP_C_OPENSTACK] = rump.NewRumpCCompiler("compilers-rump-c-hw", rump.CreateImageQemu)
   379  
   380  	//osv java
   381  	osvJavaXenCompiler := &osv.OSvJavaCompiler{
   382  		ImageFinisher: &osv.AwsImageFinisher{},
   383  	}
   384  	_compilers[compilers.OSV_JAVA_XEN] = osvJavaXenCompiler
   385  	_compilers[compilers.OSV_JAVA_AWS] = osvJavaXenCompiler
   386  	_compilers[compilers.OSV_JAVA_VIRTUALBOX] = &osv.OSvJavaCompiler{
   387  		ImageFinisher: &osv.VirtualboxImageFinisher{},
   388  	}
   389  	_compilers[compilers.OSV_JAVA_VSPHERE] = &osv.OSvJavaCompiler{
   390  		ImageFinisher: &osv.VmwareImageFinisher{},
   391  	}
   392  	// At the moment OpenStack provider borrows Xen's compiler.
   393  	_compilers[compilers.OSV_JAVA_OPENSTACK] = osvJavaXenCompiler
   394  
   395  	// osv nodejs
   396  	osvNodeQemuCompiler := &osv.OSvNodeCompiler{
   397  		ImageFinisher: &osv.QemuImageFinisher{},
   398  	}
   399  	_compilers[compilers.OSV_NODEJS_QEMU] = osvNodeQemuCompiler
   400  	_compilers[compilers.OSV_NODEJS_OPENSTACK] = osvNodeQemuCompiler
   401  	_compilers[compilers.OSV_NODEJS_AWS] = &osv.OSvNodeCompiler{
   402  		ImageFinisher: &osv.AwsImageFinisher{},
   403  	}
   404  
   405  	// osv native
   406  	osvNativeQemuCompiler := &osv.OSvNativeCompiler{
   407  		ImageFinisher: &osv.QemuImageFinisher{},
   408  	}
   409  	_compilers[compilers.OSV_NATIVE_QEMU] = osvNativeQemuCompiler
   410  	_compilers[compilers.OSV_NATIVE_OPENSTACK] = osvNativeQemuCompiler
   411  	_compilers[compilers.OSV_NATIVE_AWS] = &osv.OSvNativeCompiler{
   412  		ImageFinisher: &osv.AwsImageFinisher{},
   413  	}
   414  	_compilers[compilers.FIRECRACKER_GO] = &firecrackercompiler.FirecrackerCompiler{}
   415  
   416  	d := &UnikDaemon{
   417  		server:    lxmartini.QuietMartini(),
   418  		providers: _providers,
   419  		compilers: _compilers,
   420  	}
   421  
   422  	d.initialize()
   423  
   424  	return d, nil
   425  }
   426  
   427  func (d *UnikDaemon) Run(port int) {
   428  	d.server.RunOnAddr(fmt.Sprintf(":%v", port))
   429  }
   430  
   431  func (d *UnikDaemon) Stop() error {
   432  	// return d.server.Stop()
   433  	return nil
   434  }
   435  
   436  func (d *UnikDaemon) initialize() {
   437  	handle := func(res http.ResponseWriter, action func() (interface{}, int, error)) {
   438  		jsonObject, statusCode, err := action()
   439  		res.WriteHeader(statusCode)
   440  		if err != nil {
   441  			if err := respond(res, err); err != nil {
   442  				logrus.WithError(err).Errorf("failed to reply to http request")
   443  			}
   444  			logrus.WithError(err).Errorf("error handling request")
   445  			return
   446  		}
   447  		if jsonObject != nil {
   448  			if err := respond(res, jsonObject); err != nil {
   449  				logrus.WithError(err).Errorf("failed to reply to http request")
   450  			}
   451  			logrus.WithField("result", jsonObject).Debugf("request finished")
   452  		}
   453  	}
   454  
   455  	//images
   456  	d.server.Get("/images", func(res http.ResponseWriter, req *http.Request) {
   457  		handle(res, func() (interface{}, int, error) {
   458  			allImages := []*types.Image{}
   459  			for _, provider := range d.providers {
   460  				images, err := provider.ListImages()
   461  				if err != nil {
   462  					return nil, http.StatusInternalServerError, errors.New("could not get image list", err)
   463  				}
   464  				allImages = append(allImages, images...)
   465  			}
   466  			logrus.WithFields(logrus.Fields{
   467  				"images": allImages,
   468  			}).Debugf("Listing all images")
   469  			return allImages, http.StatusOK, nil
   470  		})
   471  	})
   472  	d.server.Get("/images/:image_name", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   473  		handle(res, func() (interface{}, int, error) {
   474  			imageName := params["image_name"]
   475  			provider, err := d.providers.ProviderForImage(imageName)
   476  			if err != nil {
   477  				return nil, http.StatusInternalServerError, err
   478  			}
   479  			image, err := provider.GetImage(imageName)
   480  			if err != nil {
   481  				return nil, http.StatusInternalServerError, err
   482  			}
   483  			return image, http.StatusOK, nil
   484  		})
   485  	})
   486  	d.server.Post("/images/:name/create", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   487  		handle(res, func() (interface{}, int, error) {
   488  			name := params["name"]
   489  			if name == "" {
   490  				return nil, http.StatusBadRequest, errors.New("image must be named", nil)
   491  			}
   492  			err := req.ParseMultipartForm(0)
   493  			if err != nil {
   494  				return nil, http.StatusInternalServerError, err
   495  			}
   496  			logrus.WithFields(logrus.Fields{
   497  				"req": req,
   498  			}).Debugf("parsing multipart form")
   499  			logrus.WithFields(logrus.Fields{
   500  				"form": req.Form,
   501  			}).Debugf("parsing form file marked 'tarfile'")
   502  			sourceTar, _, err := req.FormFile("tarfile")
   503  			if err != nil {
   504  				return nil, http.StatusBadRequest, errors.New("parsing form file marked 'tarfile", err)
   505  			}
   506  			defer sourceTar.Close()
   507  
   508  			noCleanupStr := req.FormValue("no_cleanup")
   509  			var noCleanup bool
   510  			if strings.ToLower(noCleanupStr) == "true" {
   511  				noCleanup = true
   512  			}
   513  
   514  			sourcesDir, err := ioutil.TempDir("", "unpacked.sources.dir.")
   515  			if err != nil {
   516  				return nil, http.StatusInternalServerError, errors.New("creating tmp dir for src files", err)
   517  			}
   518  
   519  			if !noCleanup {
   520  				defer os.RemoveAll(sourcesDir)
   521  			}
   522  
   523  			logrus.Debugf("extracting uploaded files to " + sourcesDir)
   524  			if err := unikos.ExtractTar(sourceTar, sourcesDir); err != nil {
   525  				return nil, http.StatusInternalServerError, errors.New("extracting sources", err)
   526  			}
   527  			forceStr := req.FormValue("force")
   528  			var force bool
   529  			if strings.ToLower(forceStr) == "true" {
   530  				force = true
   531  			}
   532  			args := req.FormValue("args")
   533  			providerName := req.FormValue("provider")
   534  			if _, ok := d.providers[providerName]; !ok {
   535  				return nil, http.StatusBadRequest, errors.New(providerName+" is not a known provider. Available: "+strings.Join(d.providers.Keys(), "|"), nil)
   536  			}
   537  
   538  			base := req.FormValue("base")
   539  			if base == "" {
   540  				return nil, http.StatusBadRequest, errors.New("must provide 'base' parameter", nil)
   541  			}
   542  			lang := req.FormValue("lang")
   543  			if lang == "" {
   544  				return nil, http.StatusBadRequest, errors.New("must provide 'lang' parameter", nil)
   545  			}
   546  			compilerName, err := compilers.ValidateCompiler(base, lang, providerName)
   547  			if err != nil {
   548  				return nil, http.StatusBadRequest, errors.New("invalid base - lang - provider match", err)
   549  			}
   550  
   551  			compiler, ok := d.compilers[compilerName]
   552  			if !ok {
   553  				return nil, http.StatusBadRequest, errors.New("unikernel type "+compilerName.String()+" not available for "+providerName+"infrastructure", nil)
   554  			}
   555  			mntStr := req.FormValue("mounts")
   556  
   557  			var mountPoints []string
   558  			if len(mntStr) > 0 {
   559  				mountPoints = strings.Split(mntStr, ",")
   560  			}
   561  
   562  			logrus.WithFields(logrus.Fields{
   563  				"force":        force,
   564  				"mount-points": mountPoints,
   565  				"name":         name,
   566  				"args":         args,
   567  				"compiler":     compilerName,
   568  				"provider":     providerName,
   569  				"noCleanup":    noCleanup,
   570  			}).Debugf("compiling raw image")
   571  
   572  			compileParams := types.CompileImageParams{
   573  				SourcesDir: sourcesDir,
   574  				Args:       args,
   575  				MntPoints:  mountPoints,
   576  				NoCleanup:  noCleanup,
   577  			}
   578  
   579  			rawImage, err := compiler.CompileRawImage(compileParams)
   580  			if err != nil {
   581  				return nil, http.StatusInternalServerError, errors.New("failed to compile raw image", err)
   582  			}
   583  			logrus.Debugf("raw image compiled and saved to " + rawImage.LocalImagePath)
   584  
   585  			if !noCleanup {
   586  				defer os.Remove(rawImage.LocalImagePath)
   587  			}
   588  
   589  			stageParams := types.StageImageParams{
   590  				Name:      name,
   591  				RawImage:  rawImage,
   592  				Force:     force,
   593  				NoCleanup: noCleanup,
   594  			}
   595  
   596  			image, err := d.providers[providerName].Stage(stageParams)
   597  			if err != nil {
   598  				return nil, http.StatusInternalServerError, errors.New("failed staging image", err)
   599  			}
   600  			return image, http.StatusCreated, nil
   601  		})
   602  	})
   603  	d.server.Delete("/images/:image_name", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   604  		handle(res, func() (interface{}, int, error) {
   605  			imageName := params["image_name"]
   606  			if imageName == "" {
   607  				logrus.WithFields(logrus.Fields{
   608  					"request": fmt.Sprintf("%v", req),
   609  				}).Errorf("image must be named")
   610  				return nil, http.StatusBadRequest, errors.New("image must be named", nil)
   611  			}
   612  			logrus.WithFields(logrus.Fields{
   613  				"request": req,
   614  			}).Infof("deleting image " + imageName)
   615  			forceStr := req.URL.Query().Get("force")
   616  			force := false
   617  			if strings.ToLower(forceStr) == "true" {
   618  				force = true
   619  			}
   620  			provider, err := d.providers.ProviderForImage(imageName)
   621  			if err != nil {
   622  				return nil, http.StatusInternalServerError, err
   623  			}
   624  			err = provider.DeleteImage(imageName, force)
   625  			if err != nil {
   626  				return nil, http.StatusInternalServerError, err
   627  			}
   628  			return nil, http.StatusNoContent, nil
   629  		})
   630  	})
   631  	d.server.Post("/images/push/:image_name", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   632  		handle(res, func() (interface{}, int, error) {
   633  			imageName := params["image_name"]
   634  			if imageName == "" {
   635  				logrus.WithFields(logrus.Fields{
   636  					"request": fmt.Sprintf("%v", req),
   637  				}).Errorf("image must be named")
   638  				return nil, http.StatusBadRequest, errors.New("image must be named", nil)
   639  			}
   640  			var c config.HubConfig
   641  			body, err := ioutil.ReadAll(req.Body)
   642  			if err != nil {
   643  				return nil, http.StatusBadRequest, errors.New("could not read request body", err)
   644  			}
   645  			if err := json.Unmarshal(body, &c); err != nil {
   646  				return nil, http.StatusBadRequest, errors.New("failed to parse request json", err)
   647  			}
   648  			logrus.WithFields(logrus.Fields{
   649  				"request": req,
   650  			}).Infof("pushing image " + imageName + " to " + c.URL)
   651  			provider, err := d.providers.ProviderForImage(imageName)
   652  			if err != nil {
   653  				return nil, http.StatusInternalServerError, err
   654  			}
   655  			err = provider.PushImage(types.PushImagePararms{
   656  				ImageName: imageName,
   657  				Config:    c,
   658  			})
   659  			if err != nil {
   660  				return nil, http.StatusInternalServerError, err
   661  			}
   662  			return nil, http.StatusAccepted, nil
   663  		})
   664  	})
   665  	d.server.Post("/images/pull/:image_name", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   666  		handle(res, func() (interface{}, int, error) {
   667  			imageName := params["image_name"]
   668  			if imageName == "" {
   669  				logrus.WithFields(logrus.Fields{
   670  					"request": fmt.Sprintf("%v", req),
   671  				}).Errorf("image must be named")
   672  				return nil, http.StatusBadRequest, errors.New("image must be named", nil)
   673  			}
   674  			var c config.HubConfig
   675  			body, err := ioutil.ReadAll(req.Body)
   676  			if err != nil {
   677  				return nil, http.StatusBadRequest, errors.New("could not read request body", err)
   678  			}
   679  			if err := json.Unmarshal(body, &c); err != nil {
   680  				return nil, http.StatusBadRequest, errors.New("failed to parse request json", err)
   681  			}
   682  			logrus.WithFields(logrus.Fields{
   683  				"request": req,
   684  			}).Infof("pushing image " + imageName + " to " + c.URL)
   685  			providerName := req.URL.Query().Get("provider")
   686  			provider, ok := d.providers[providerName]
   687  			if !ok {
   688  				return nil, http.StatusBadRequest, errors.New(providerName+" is not a known provider. Available: "+strings.Join(d.providers.Keys(), "|"), nil)
   689  			}
   690  			forceStr := req.URL.Query().Get("force")
   691  			force := false
   692  			if strings.ToLower(forceStr) == "true" {
   693  				force = true
   694  			}
   695  			err = provider.PullImage(types.PullImagePararms{
   696  				ImageName: imageName,
   697  				Config:    c,
   698  				Force:     force,
   699  			})
   700  			if err != nil {
   701  				return nil, http.StatusInternalServerError, err
   702  			}
   703  			return nil, http.StatusAccepted, nil
   704  		})
   705  	})
   706  	d.server.Post("/images/remote-delete/:image_name", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   707  		handle(res, func() (interface{}, int, error) {
   708  			imageName := params["image_name"]
   709  			if imageName == "" {
   710  				logrus.WithFields(logrus.Fields{
   711  					"request": fmt.Sprintf("%v", req),
   712  				}).Errorf("image must be named")
   713  				return nil, http.StatusBadRequest, errors.New("image must be named", nil)
   714  			}
   715  			var c config.HubConfig
   716  			body, err := ioutil.ReadAll(req.Body)
   717  			if err != nil {
   718  				return nil, http.StatusBadRequest, errors.New("could not read request body", err)
   719  			}
   720  			if err := json.Unmarshal(body, &c); err != nil {
   721  				return nil, http.StatusBadRequest, errors.New("failed to parse request json", err)
   722  			}
   723  			logrus.WithFields(logrus.Fields{
   724  				"request": req,
   725  			}).Infof("deleting image " + imageName + " to " + c.URL)
   726  			provider, err := d.providers.ProviderForImage(imageName)
   727  			if err != nil {
   728  				return nil, http.StatusInternalServerError, err
   729  			}
   730  			err = provider.PushImage(types.PushImagePararms{
   731  				ImageName: imageName,
   732  				Config:    c,
   733  			})
   734  			if err != nil {
   735  				return nil, http.StatusInternalServerError, err
   736  			}
   737  			return nil, http.StatusAccepted, nil
   738  		})
   739  	})
   740  
   741  	//Instances
   742  	d.server.Get("/instances", func(res http.ResponseWriter, req *http.Request) {
   743  		handle(res, func() (interface{}, int, error) {
   744  			allInstances := []*types.Instance{}
   745  			for _, provider := range d.providers {
   746  				instances, err := provider.ListInstances()
   747  				if err != nil {
   748  					return nil, http.StatusInternalServerError, errors.New("could not get instance list", err)
   749  				}
   750  				allInstances = append(allInstances, instances...)
   751  			}
   752  			logrus.WithFields(logrus.Fields{
   753  				"instances": allInstances,
   754  			}).Debugf("Listing all instances")
   755  			return allInstances, http.StatusOK, nil
   756  		})
   757  	})
   758  	d.server.Get("/instances/:instance_id", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   759  		handle(res, func() (interface{}, int, error) {
   760  			instanceId := params["instance_id"]
   761  			provider, err := d.providers.ProviderForInstance(instanceId)
   762  			if err != nil {
   763  				return nil, http.StatusInternalServerError, err
   764  			}
   765  			instance, err := provider.GetInstance(instanceId)
   766  			if err != nil {
   767  				return nil, http.StatusInternalServerError, err
   768  			}
   769  			return instance, http.StatusOK, nil
   770  		})
   771  	})
   772  	d.server.Delete("/instances/:instance_id", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   773  		handle(res, func() (interface{}, int, error) {
   774  			instanceId := params["instance_id"]
   775  			logrus.WithFields(logrus.Fields{
   776  				"request": req,
   777  			}).Infof("deleting instance " + instanceId)
   778  			provider, err := d.providers.ProviderForInstance(instanceId)
   779  			if err != nil {
   780  				return nil, http.StatusInternalServerError, err
   781  			}
   782  			forceStr := req.URL.Query().Get("force")
   783  			force := false
   784  			if strings.ToLower(forceStr) == "true" {
   785  				force = true
   786  			}
   787  			err = provider.DeleteInstance(instanceId, force)
   788  			if err != nil {
   789  				return nil, http.StatusInternalServerError, err
   790  			}
   791  			return nil, http.StatusNoContent, nil
   792  		})
   793  	})
   794  	d.server.Get("/instances/:instance_id/logs", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   795  		handle(res, func() (interface{}, int, error) {
   796  			instanceId := params["instance_id"]
   797  			follow := req.URL.Query().Get("follow")
   798  			res.Write([]byte("getting logs for " + instanceId + "...\n"))
   799  			provider, err := d.providers.ProviderForInstance(instanceId)
   800  			if err != nil {
   801  				return nil, http.StatusInternalServerError, err
   802  			}
   803  			if strings.ToLower(follow) == "true" {
   804  				if f, ok := res.(http.Flusher); ok {
   805  					f.Flush()
   806  				} else {
   807  					return nil, http.StatusInternalServerError, errors.New("not a flusher", nil)
   808  				}
   809  
   810  				deleteOnDisconnect := req.URL.Query().Get("delete")
   811  				if strings.ToLower(deleteOnDisconnect) == "true" {
   812  					logrus.Warnf("INSTANCE %v WILL BE TERMINTED ON CLIENT DISCONNECT!", instanceId)
   813  					defer provider.DeleteInstance(instanceId, true)
   814  				}
   815  
   816  				output := ioutils.NewWriteFlusher(res)
   817  				logFn := func() (string, error) {
   818  					return provider.GetInstanceLogs(instanceId)
   819  				}
   820  				if err := streamOutput(logFn, output); err != nil {
   821  					logrus.WithError(err).WithFields(logrus.Fields{
   822  						"instanceId": instanceId,
   823  					}).Warnf("streaming logs stopped")
   824  					return nil, http.StatusInternalServerError, err
   825  				}
   826  				return nil, 0, nil
   827  			}
   828  			logs, err := provider.GetInstanceLogs(instanceId)
   829  			if err != nil {
   830  				return nil, http.StatusInternalServerError, errors.New("failed to perform get logs request", err)
   831  			}
   832  			return logs, http.StatusOK, nil
   833  		})
   834  	})
   835  	d.server.Post("/instances/run", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   836  		handle(res, func() (interface{}, int, error) {
   837  			body, err := ioutil.ReadAll(req.Body)
   838  			if err != nil {
   839  				return nil, http.StatusBadRequest, errors.New("could not read request body", err)
   840  			}
   841  			defer req.Body.Close()
   842  			var runInstanceRequest RunInstanceRequest
   843  			if err := json.Unmarshal(body, &runInstanceRequest); err != nil {
   844  				return nil, http.StatusBadRequest, errors.New("failed to parse request json", err)
   845  			}
   846  
   847  			logrus.WithFields(logrus.Fields{
   848  				"request": runInstanceRequest,
   849  			}).Debugf("Received run request")
   850  
   851  			if runInstanceRequest.ImageName == "" {
   852  				return nil, http.StatusBadRequest, errors.New("image must be named", nil)
   853  			}
   854  
   855  			provider, err := d.providers.ProviderForImage(runInstanceRequest.ImageName)
   856  			if err != nil {
   857  				return nil, http.StatusBadRequest, err
   858  			}
   859  
   860  			params := types.RunInstanceParams{
   861  				Name:                 runInstanceRequest.InstanceName,
   862  				ImageId:              runInstanceRequest.ImageName,
   863  				MntPointsToVolumeIds: runInstanceRequest.Mounts,
   864  				Env:                  runInstanceRequest.Env,
   865  				InstanceMemory:       runInstanceRequest.MemoryMb,
   866  				NoCleanup:            runInstanceRequest.NoCleanup,
   867  				DebugMode:            runInstanceRequest.DebugMode,
   868  			}
   869  
   870  			instance, err := provider.RunInstance(params)
   871  			if err != nil {
   872  				return nil, http.StatusInternalServerError, err
   873  			}
   874  			return instance, http.StatusCreated, nil
   875  		})
   876  	})
   877  	d.server.Post("/instances/:instance_id/start", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   878  		handle(res, func() (interface{}, int, error) {
   879  			instanceId := params["instance_id"]
   880  			logrus.WithFields(logrus.Fields{
   881  				"request": req,
   882  			}).Infof("starting instance " + instanceId)
   883  			provider, err := d.providers.ProviderForInstance(instanceId)
   884  			if err != nil {
   885  				return nil, http.StatusInternalServerError, err
   886  			}
   887  			err = provider.StartInstance(instanceId)
   888  			if err != nil {
   889  				return nil, http.StatusInternalServerError, errors.New("could not start instance "+instanceId, err)
   890  			}
   891  			return nil, http.StatusOK, nil
   892  		})
   893  	})
   894  	d.server.Post("/instances/:instance_id/stop", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   895  		handle(res, func() (interface{}, int, error) {
   896  			instanceId := params["instance_id"]
   897  			logrus.WithFields(logrus.Fields{
   898  				"request": req,
   899  			}).Infof("stopping instance " + instanceId)
   900  			provider, err := d.providers.ProviderForInstance(instanceId)
   901  			if err != nil {
   902  				return nil, http.StatusInternalServerError, err
   903  			}
   904  			err = provider.StopInstance(instanceId)
   905  			if err != nil {
   906  				return nil, http.StatusInternalServerError, errors.New("could not stop instance "+instanceId, err)
   907  			}
   908  			return nil, http.StatusOK, nil
   909  		})
   910  	})
   911  
   912  	//Volumes
   913  	d.server.Get("/volumes", func(res http.ResponseWriter, req *http.Request) {
   914  		handle(res, func() (interface{}, int, error) {
   915  			logrus.Debugf("listing volumes started")
   916  			allVolumes := []*types.Volume{}
   917  			for _, provider := range d.providers {
   918  				volumes, err := provider.ListVolumes()
   919  				if err != nil {
   920  					return nil, http.StatusInternalServerError, errors.New("could not retrieve volumes", err)
   921  				}
   922  				allVolumes = append(allVolumes, volumes...)
   923  			}
   924  			logrus.WithFields(logrus.Fields{
   925  				"volumes": allVolumes,
   926  			}).Infof("volumes")
   927  			return allVolumes, http.StatusOK, nil
   928  		})
   929  	})
   930  	d.server.Get("/volumes/:volume_name", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   931  		handle(res, func() (interface{}, int, error) {
   932  			volumeName := params["volume_name"]
   933  			provider, err := d.providers.ProviderForVolume(volumeName)
   934  			if err != nil {
   935  				return nil, http.StatusInternalServerError, err
   936  			}
   937  			volume, err := provider.GetVolume(volumeName)
   938  			if err != nil {
   939  				return nil, http.StatusInternalServerError, errors.New("could not get volume", err)
   940  			}
   941  			logrus.WithFields(logrus.Fields{
   942  				"volume": volume,
   943  			}).Infof("volume retrieved")
   944  			return volume, http.StatusOK, nil
   945  		})
   946  	})
   947  	d.server.Post("/volumes/:volume_name", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
   948  		handle(res, func() (interface{}, int, error) {
   949  			volumeName := params["volume_name"]
   950  			var imagePath string
   951  			var provider providers.Provider
   952  			var noCleanup bool
   953  			var raw bool
   954  
   955  			logrus.WithField("req", req).Info("received request to create volume")
   956  
   957  			typeStr := req.FormValue("type")
   958  			typeStr = strings.ToLower(typeStr)
   959  
   960  			if strings.Contains(req.Header.Get("Content-type"), "multipart/form-data") {
   961  
   962  				if strings.ToLower(req.FormValue("raw")) == "true" {
   963  					raw = true
   964  				}
   965  				if strings.ToLower(req.FormValue("no_cleanup")) == "true" {
   966  					noCleanup = true
   967  				}
   968  
   969  				logrus.Info("received request with form-data")
   970  				err := req.ParseMultipartForm(0)
   971  				if err != nil {
   972  					return nil, http.StatusInternalServerError, err
   973  				}
   974  				logrus.WithFields(logrus.Fields{
   975  					"req": req,
   976  				}).Debugf("parsing multipart form")
   977  
   978  				providerName := req.FormValue("provider")
   979  				if _, ok := d.providers[providerName]; !ok {
   980  					return nil, http.StatusBadRequest, errors.New(providerName+" is not a known provider. Available: "+strings.Join(d.providers.Keys(), "|"), nil)
   981  				}
   982  				provider = d.providers[providerName]
   983  				dataTar, header, err := req.FormFile("tarfile")
   984  				if err != nil {
   985  					return nil, http.StatusInternalServerError, errors.New("failed to retrieve form-data for tarfe", err)
   986  				}
   987  				defer dataTar.Close()
   988  
   989  				if !raw {
   990  					logrus.WithFields(logrus.Fields{
   991  						"form": req.Form,
   992  					}).Debugf("seeking form file marked 'tarfile'")
   993  					logrus.WithFields(logrus.Fields{
   994  						"tarred-data": header.Filename,
   995  						"name":        volumeName,
   996  						"provider":    providerName,
   997  					}).Debugf("creating volume started")
   998  
   999  					sizeStr := req.URL.Query().Get("size")
  1000  					if sizeStr == "" {
  1001  						sizeStr = "0"
  1002  					}
  1003  					size, err := strconv.Atoi(sizeStr)
  1004  					if err != nil {
  1005  						return nil, http.StatusBadRequest, errors.New("could not parse given size", err)
  1006  					}
  1007  					imagePath, err = util.BuildRawDataImageWithType(dataTar, unikos.MegaBytes(size), typeStr, provider.GetConfig().UsePartitionTables)
  1008  					if err != nil {
  1009  						return nil, http.StatusInternalServerError, errors.New("creating raw volume image", err)
  1010  					}
  1011  				} else {
  1012  					imagePathFile, err := ioutil.TempFile("", "")
  1013  					if err != nil {
  1014  						return nil, http.StatusInternalServerError, errors.New("creating temp file for volume image", err)
  1015  					}
  1016  					_, err = io.Copy(imagePathFile, dataTar)
  1017  					imagePathFile.Close()
  1018  					if err != nil {
  1019  						return nil, http.StatusInternalServerError, errors.New("creating temp file for volume image", err)
  1020  					}
  1021  					imagePath = imagePathFile.Name()
  1022  				}
  1023  
  1024  			} else {
  1025  				if strings.ToLower(req.URL.Query().Get("raw")) == "true" {
  1026  					raw = true
  1027  				}
  1028  				if strings.ToLower(req.URL.Query().Get("no_cleanup")) == "true" {
  1029  					noCleanup = true
  1030  				}
  1031  
  1032  				if raw == true {
  1033  					return nil, http.StatusBadRequest, errors.New("Raw volume was requested but no data provided", nil)
  1034  				}
  1035  				logrus.Info("received request for empty volume")
  1036  				sizeStr := req.URL.Query().Get("size")
  1037  				size, err := strconv.Atoi(sizeStr)
  1038  				if err != nil {
  1039  					return nil, http.StatusBadRequest, errors.New("could not parse given size", err)
  1040  				}
  1041  				logrus.WithFields(logrus.Fields{
  1042  					"size": size,
  1043  					"name": volumeName,
  1044  				}).Debugf("creating empty volume started")
  1045  				imagePath, err = util.BuildEmptyDataVolumeWithType(unikos.MegaBytes(size), typeStr)
  1046  				if err != nil {
  1047  					return nil, http.StatusInternalServerError, errors.New("failed building raw image", err)
  1048  				}
  1049  				providerName := req.URL.Query().Get("provider")
  1050  				if _, ok := d.providers[providerName]; !ok {
  1051  					return nil, http.StatusBadRequest, errors.New(providerName+" is not a known provider. Available: "+strings.Join(d.providers.Keys(), "|"), nil)
  1052  				}
  1053  				provider = d.providers[providerName]
  1054  				logrus.WithFields(logrus.Fields{
  1055  					"image": imagePath,
  1056  				}).Infof("raw image created")
  1057  
  1058  			}
  1059  
  1060  			if !noCleanup {
  1061  				defer os.RemoveAll(imagePath)
  1062  			}
  1063  
  1064  			params := types.CreateVolumeParams{
  1065  				Name:      volumeName,
  1066  				ImagePath: imagePath,
  1067  				NoCleanup: noCleanup,
  1068  			}
  1069  
  1070  			volume, err := provider.CreateVolume(params)
  1071  			if err != nil {
  1072  				return nil, http.StatusInternalServerError, errors.New("could not create volume", err)
  1073  			}
  1074  			logrus.WithFields(logrus.Fields{
  1075  				"volume": volume,
  1076  			}).Infof("volume created")
  1077  			return volume, http.StatusCreated, nil
  1078  		})
  1079  	})
  1080  	d.server.Delete("/volumes/:volume_name", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
  1081  		handle(res, func() (interface{}, int, error) {
  1082  			volumeName := params["volume_name"]
  1083  			provider, err := d.providers.ProviderForVolume(volumeName)
  1084  			if err != nil {
  1085  				return nil, http.StatusInternalServerError, err
  1086  			}
  1087  			forceStr := req.URL.Query().Get("force")
  1088  			force := false
  1089  			if strings.ToLower(forceStr) == "true" {
  1090  				force = true
  1091  			}
  1092  
  1093  			logrus.WithFields(logrus.Fields{
  1094  				"force": force, "name": volumeName,
  1095  			}).Debugf("deleting volume started")
  1096  			err = provider.DeleteVolume(volumeName, force)
  1097  			if err != nil {
  1098  				return nil, http.StatusInternalServerError, errors.New("could not delete volume", err)
  1099  			}
  1100  			logrus.WithFields(logrus.Fields{
  1101  				"volume": volumeName,
  1102  			}).Infof("volume deleted")
  1103  			return nil, http.StatusNoContent, nil
  1104  		})
  1105  	})
  1106  	d.server.Post("/volumes/:volume_name/attach/:instance_id", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
  1107  		handle(res, func() (interface{}, int, error) {
  1108  			volumeName := params["volume_name"]
  1109  			provider, err := d.providers.ProviderForVolume(volumeName)
  1110  			if err != nil {
  1111  				return nil, http.StatusInternalServerError, err
  1112  			}
  1113  			instanceId := params["instance_id"]
  1114  			mount := req.URL.Query().Get("mount")
  1115  			if mount == "" {
  1116  				return nil, http.StatusBadRequest, errors.New("must provide a mount point in URL query", nil)
  1117  			}
  1118  			logrus.WithFields(logrus.Fields{
  1119  				"instance": instanceId,
  1120  				"volume":   volumeName,
  1121  				"mount":    mount,
  1122  			}).Debugf("attaching volume to instance")
  1123  			err = provider.AttachVolume(volumeName, instanceId, mount)
  1124  			if err != nil {
  1125  				return nil, http.StatusInternalServerError, errors.New("could not attach volume to instance", err)
  1126  			}
  1127  			logrus.WithFields(logrus.Fields{
  1128  				"instance": instanceId,
  1129  				"volume":   volumeName,
  1130  				"mount":    mount,
  1131  			}).Infof("volume attached")
  1132  			return volumeName, http.StatusAccepted, nil
  1133  		})
  1134  	})
  1135  	d.server.Post("/volumes/:volume_name/detach", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
  1136  		handle(res, func() (interface{}, int, error) {
  1137  			volumeName := params["volume_name"]
  1138  			provider, err := d.providers.ProviderForVolume(volumeName)
  1139  			if err != nil {
  1140  				return nil, http.StatusInternalServerError, err
  1141  			}
  1142  			logrus.WithFields(logrus.Fields{
  1143  				"volume": volumeName,
  1144  			}).Debugf("detaching volume from any instance")
  1145  			err = provider.DetachVolume(volumeName)
  1146  			if err != nil {
  1147  				return nil, http.StatusInternalServerError, errors.New("could not detach volume from instance", err)
  1148  			}
  1149  			logrus.WithFields(logrus.Fields{
  1150  				"volume": volumeName,
  1151  			}).Infof("volume detached")
  1152  			return volumeName, http.StatusAccepted, nil
  1153  		})
  1154  	})
  1155  
  1156  	//info
  1157  	d.server.Get("/available_compilers", func(res http.ResponseWriter, req *http.Request) {
  1158  		handle(res, func() (interface{}, int, error) {
  1159  			logrus.Debugf("listing available compilers")
  1160  			availableCompilers := sort.StringSlice{}
  1161  			for compilerName := range d.compilers {
  1162  				availableCompilers = append(availableCompilers, compilerName.String())
  1163  			}
  1164  			availableCompilers.Sort()
  1165  			logrus.WithFields(logrus.Fields{
  1166  				"compilers": availableCompilers,
  1167  			}).Infof("compilers")
  1168  			return []string(availableCompilers), http.StatusOK, nil
  1169  		})
  1170  	})
  1171  	d.server.Get("/available_providers", func(res http.ResponseWriter, req *http.Request) {
  1172  		handle(res, func() (interface{}, int, error) {
  1173  			logrus.Debugf("listing available providers")
  1174  			availableProviders := sort.StringSlice{}
  1175  			for compilerName := range d.providers {
  1176  				availableProviders = append(availableProviders, compilerName)
  1177  			}
  1178  			availableProviders.Sort()
  1179  			logrus.WithFields(logrus.Fields{
  1180  				"providers": availableProviders,
  1181  			}).Infof("providers")
  1182  			return []string(availableProviders), http.StatusOK, nil
  1183  		})
  1184  	})
  1185  	d.server.Get("/describe_compiler", func(res http.ResponseWriter, req *http.Request) {
  1186  		handle(res, func() (interface{}, int, error) {
  1187  			logrus.Debugf("describing compiler")
  1188  
  1189  			// Find compiler.
  1190  			provider := req.FormValue("provider")
  1191  			if _, ok := d.providers[provider]; !ok {
  1192  				return nil, http.StatusBadRequest, errors.New(provider+" is not a known provider. Available: "+
  1193  					strings.Join(d.providers.Keys(), "|"), nil)
  1194  			}
  1195  			base := req.FormValue("base")
  1196  			if base == "" {
  1197  				return nil, http.StatusBadRequest, errors.New("must provide 'base' parameter", nil)
  1198  			}
  1199  			lang := req.FormValue("lang")
  1200  			if lang == "" {
  1201  				return nil, http.StatusBadRequest, errors.New("must provide 'lang' parameter", nil)
  1202  			}
  1203  			compilerName, err := compilers.ValidateCompiler(base, lang, provider)
  1204  			if err != nil {
  1205  				return nil, http.StatusBadRequest, errors.New("invalid base - lang - provider match", err)
  1206  			}
  1207  			compiler, ok := d.compilers[compilerName]
  1208  			if !ok {
  1209  				return nil, http.StatusBadRequest, errors.New("failed to access compiler", nil)
  1210  			}
  1211  
  1212  			logrus.WithFields(logrus.Fields{
  1213  				"compiler": compiler,
  1214  			}).Infof("describe compiler")
  1215  
  1216  			// Let compiler describe itself.
  1217  			compilerUsage := compiler.Usage()
  1218  			description := "<missing compiler description>"
  1219  			if compilerUsage != nil {
  1220  				description = compilerUsage.ToString()
  1221  			}
  1222  
  1223  			return description, http.StatusOK, nil
  1224  		})
  1225  	})
  1226  }
  1227  
  1228  func streamOutput(outputFunc func() (string, error), w io.Writer) (err error) {
  1229  	defer func() {
  1230  		if err != nil {
  1231  			w.Write([]byte(err.Error()))
  1232  		}
  1233  	}()
  1234  
  1235  	//give instance some time to start logs server
  1236  	if err := util.Retry(30, time.Millisecond*500, func() error {
  1237  		_, err := outputFunc()
  1238  		return err
  1239  	}); err != nil {
  1240  		return err
  1241  	}
  1242  	linesCounted := -1
  1243  	for {
  1244  		time.Sleep(100 * time.Millisecond)
  1245  		output, err := outputFunc()
  1246  		if err != nil {
  1247  			return errors.New("could not read output", err)
  1248  		}
  1249  		logLines := strings.Split(output, "\n")
  1250  		for i, _ := range logLines {
  1251  			if linesCounted < len(logLines) && linesCounted < i {
  1252  				linesCounted = i
  1253  
  1254  				if f, ok := w.(http.Flusher); ok {
  1255  					f.Flush()
  1256  				} else {
  1257  					return errors.New("w is not a flusher", nil)
  1258  				}
  1259  
  1260  				_, err = w.Write([]byte(logLines[linesCounted] + "\n"))
  1261  				if err != nil {
  1262  					return errors.New("writing to connection", err)
  1263  				}
  1264  			}
  1265  		}
  1266  		_, err = w.Write([]byte{0}) //ignore errors; close comes from external
  1267  		if err != nil {
  1268  			return nil
  1269  		}
  1270  		if len(logLines)-1 == linesCounted {
  1271  			time.Sleep(2500 * time.Millisecond)
  1272  			continue
  1273  		}
  1274  	}
  1275  }
  1276  
  1277  func respond(res http.ResponseWriter, message interface{}) error {
  1278  	switch message.(type) {
  1279  	case string:
  1280  		messageString := message.(string)
  1281  		data := []byte(messageString)
  1282  		_, err := res.Write(data)
  1283  		if err != nil {
  1284  			return errors.New("writing data", err)
  1285  		}
  1286  		return nil
  1287  	case error:
  1288  		responseError := message.(error)
  1289  		_, err := res.Write([]byte(responseError.Error()))
  1290  		if err != nil {
  1291  			return errors.New("writing data", err)
  1292  		}
  1293  		return nil
  1294  	}
  1295  	data, err := json.Marshal(message)
  1296  	if err != nil {
  1297  		return errors.New("marshalling message to json", err)
  1298  	}
  1299  	_, err = res.Write(data)
  1300  	if err != nil {
  1301  		return errors.New("writing data", err)
  1302  	}
  1303  	return nil
  1304  }