From a4f0cb9da7d860a710e7eb0991dbcf8e88d213b5 Mon Sep 17 00:00:00 2001 From: Angad Kambli Date: Thu, 22 Oct 2020 14:50:00 +0530 Subject: [PATCH] feat(*) : dockerize seaweedfs dockerozed seaweedfs, doesn't change any database behaviour as of now no breaking changes --- .gitignore | 1 + configs/types.go | 1 + helper.go | 42 +++++++++++++++++++ lib/docker/container.go | 62 +++++++++++++++++++++++++++ lib/seaweedfs/handler.go | 90 ++++++++++++++++++++++++++++++++++++++++ service_launchers.go | 12 ++++++ types/constants.go | 15 +++++++ types/container.go | 25 +++++++++++ 8 files changed, 248 insertions(+) create mode 100644 lib/seaweedfs/handler.go diff --git a/.gitignore b/.gitignore index b4dba4b8..1e3eb7fe 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ site/ coverage.txt releases/ gasper*.log +seaweed/ diff --git a/configs/types.go b/configs/types.go index 4338f5c3..455dd6f6 100644 --- a/configs/types.go +++ b/configs/types.go @@ -125,6 +125,7 @@ type Images struct { Mongodb string `toml:"mongodb"` Postgresql string `toml:"postgresql"` Redis string `toml:"redis"` + Seaweedfs string `toml:"chrislusf/seaweedfs"` } // Services is the configuration for all Services diff --git a/helper.go b/helper.go index 4eebbcac..4fc4558f 100644 --- a/helper.go +++ b/helper.go @@ -10,6 +10,7 @@ import ( "github.com/sdslabs/gasper/lib/database" "github.com/sdslabs/gasper/lib/docker" + "github.com/sdslabs/gasper/lib/seaweedfs" "github.com/sdslabs/gasper/lib/utils" "google.golang.org/grpc" ) @@ -98,3 +99,44 @@ func setupDatabaseContainer(serviceName string) { } } } + +func setupSeaweedfsContainer(serviceName string) { + containers, err := docker.ListContainers() + if err != nil { + utils.LogError("Main-Helper-18", err) + os.Exit(1) + } + + if !utils.Contains(containers, serviceName) { + utils.LogInfo("No %s instance found in host. Building the instance.", strings.Title(serviceName)) + containerID, err := seaweedfs.SetupSeaweedfsInstance(serviceName) + if err != nil { + utils.Log("There was a problem deploying %s service.", strings.Title(serviceName), utils.ErrorTAG) + utils.LogError("Main-Helper-19", err) + } else { + utils.LogInfo("%s Container has been deployed with ID:\t%s \n", strings.Title(serviceName), containerID) + } + } else { + containerStatus, err := docker.InspectContainerState(serviceName) + if err != nil { + utils.Log("Error in fetching container state. Deleting container and deploying again.", strings.Title(serviceName), utils.ErrorTAG) + utils.LogError("Main-Helper-20", err) + err := docker.DeleteContainer(serviceName) + if err != nil { + utils.LogError("Main-Helper-21", err) + } + containerID, err := seaweedfs.SetupSeaweedfsInstance(serviceName) + if err != nil { + utils.Log("There was a problem deploying %s service even after restart.", strings.Title(serviceName), utils.ErrorTAG) + utils.LogError("Main-Helper-22", err) + } else { + utils.LogInfo("Container has been deployed with ID:\t%s \n", containerID) + } + } + if !containerStatus.Running { + if err := docker.StartContainer(serviceName); err != nil { + utils.LogError("Main-Helper-23", err) + } + } + } +} diff --git a/lib/docker/container.go b/lib/docker/container.go index 1ea2e660..e06cbf36 100644 --- a/lib/docker/container.go +++ b/lib/docker/container.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "os" "time" dockerTypes "github.com/docker/docker/api/types" @@ -113,6 +114,67 @@ func CreateDatabaseContainer(containerCfg types.DatabaseContainer) (string, erro return createdConf.ID, nil } +// CreateSeaweedContainer creates a new container of the given container options, returns id of the container created +func CreateSeaweedContainer(containerCfg *types.SeaweedfsContainer) (string, error) { + ctx := context.Background() + volume := fmt.Sprintf("%s:%s", containerCfg.StoreDir, containerCfg.WorkDir) + + if containerCfg.Name == types.SeaweedFiler { + err := os.MkdirAll("seaweed/seaweed-filer-storage/filerldb2", 0777) + if err != nil { + println(err.Error()) + } + } + + envArr := []string{} + for key, value := range containerCfg.Env { + envArr = append(envArr, fmt.Sprintf("%s=%v", key, value)) + } + + containerPortRule1 := nat.Port(fmt.Sprintf(`%d/tcp`, containerCfg.HostPort1)) + containerPortRule2 := nat.Port(fmt.Sprintf(`%d/tcp`, containerCfg.HostPort2)) + + containerConfig := &container.Config{ + Image: containerCfg.Image, + ExposedPorts: nat.PortSet{ + containerPortRule1: struct{}{}, + containerPortRule2: struct{}{}, + }, + Env: envArr, + Volumes: map[string]struct{}{ + volume: {}, + }, + } + + containerConfig.Cmd = containerCfg.Cmd + + hostConfig := &container.HostConfig{ + Binds: []string{ + volume, + }, + PortBindings: nat.PortMap{ + nat.Port(containerPortRule1): []nat.PortBinding{{ + HostIP: "", + HostPort: fmt.Sprintf("%d", containerCfg.ContainerPort1)}}, + nat.Port(containerPortRule2): []nat.PortBinding{{ + HostIP: "", + HostPort: fmt.Sprintf("%d", containerCfg.ContainerPort2)}}, + }, + } + + if containerCfg.Name != types.SeaweedMaster { + hostConfig.Links = []string{ + fmt.Sprintf("%s:master", types.SeaweedMaster), + } + } + + createdConf, err := cli.ContainerCreate(ctx, containerConfig, hostConfig, nil, containerCfg.Name) + if err != nil { + return "", err + } + return createdConf.ID, nil +} + // StartContainer starts the container corresponding to given containerID func StartContainer(containerID string) error { ctx := context.Background() diff --git a/lib/seaweedfs/handler.go b/lib/seaweedfs/handler.go new file mode 100644 index 00000000..ffc8ad35 --- /dev/null +++ b/lib/seaweedfs/handler.go @@ -0,0 +1,90 @@ +package seaweedfs + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/sdslabs/gasper/lib/docker" + "github.com/sdslabs/gasper/types" +) + +var storepath, _ = os.Getwd() + +// Maps seaweedfs's service name with its appropriate configuration +var seaweedfsMap = map[string]*types.SeaweedfsContainer{ + types.SeaweedMaster: { + Image: "chrislusf/seaweedfs", + Cmd: []string{"master", "-ip=master"}, + HostPort1: 9333, + ContainerPort1: 9333, + HostPort2: 19333, + ContainerPort2: 1933, + WorkDir: "/data", + StoreDir: filepath.Join(storepath, "seaweed/seaweed-master-storage"), + Name: types.SeaweedMaster, + }, + types.SeaweedVolume: { + Image: "chrislusf/seaweedfs", + Cmd: []string{"volume", "-mserver=master:9333", "-port=8080"}, + HostPort1: 8080, + ContainerPort1: 8080, + HostPort2: 18080, + ContainerPort2: 18080, + WorkDir: "/data", + StoreDir: filepath.Join(storepath, "seaweed/seaweed-volume-storage"), + Name: types.SeaweedVolume, + }, + types.SeaweedFiler: { + Image: "chrislusf/seaweedfs", + Cmd: []string{"filer", "-master=master:9333"}, + HostPort1: 8888, + ContainerPort1: 8888, + HostPort2: 18888, + ContainerPort2: 18888, + WorkDir: "/data", + StoreDir: filepath.Join(storepath, "seaweed", "seaweed-filer-storage"), + Name: types.SeaweedFiler, + }, + types.SeaweedCronjob: { + Image: "chrislusf/seaweedfs", + Cmd: []string{"cronjob"}, + HostPort1: 8889, + ContainerPort1: 8889, + HostPort2: 18889, + ContainerPort2: 18889, + WorkDir: "/data", + StoreDir: filepath.Join(storepath, "seaweed/seaweed-cronjob-storage"), + Env: map[string]interface{}{"CRON_SCHEDULE": "*/2 * * * * *", "WEED_MASTER": "master:9333"}, + Name: types.SeaweedCronjob, + }, + types.SeaweedS3: { + Image: "chrislusf/seaweedfs", + Cmd: []string{"s3", "-filer=filer:8888"}, + HostPort1: 8333, + ContainerPort1: 8333, + HostPort2: 18898, + ContainerPort2: 18898, + WorkDir: "/data", + StoreDir: filepath.Join(storepath, "seaweed/seaweed-s3-storage"), + Name: types.SeaweedS3, + }, +} + +// SetupSeaweedfsInstance sets up containers for database +func SetupSeaweedfsInstance(seaweedType string) (string, types.ResponseError) { + if seaweedfsMap[seaweedType] == nil { + return "", types.NewResErr(500, fmt.Sprintf("Invalid seaweedfs type %s provided", seaweedType), nil) + } + + containerID, err := docker.CreateSeaweedContainer(seaweedfsMap[seaweedType]) + if err != nil { + return "", types.NewResErr(500, "container not created", err) + } + + if err := docker.StartContainer(containerID); err != nil { + return "", types.NewResErr(500, "container not started", err) + } + + return containerID, nil +} diff --git a/service_launchers.go b/service_launchers.go index 6c8d5193..2629acde 100644 --- a/service_launchers.go +++ b/service_launchers.go @@ -92,6 +92,18 @@ func startAppMakerService() error { } func startMasterService() error { + + checkAndPullImages("chrislusf/seaweedfs") + err := os.MkdirAll("seaweed/seaweed-filer-storage/filerldb2", 0777) + if err != nil { + println(err.Error()) + } + setupSeaweedfsContainer(types.SeaweedMaster) + setupSeaweedfsContainer(types.SeaweedVolume) + setupSeaweedfsContainer(types.SeaweedFiler) + setupSeaweedfsContainer(types.SeaweedCronjob) + setupSeaweedfsContainer(types.SeaweedS3) + if configs.ServiceConfig.Master.MongoDB.PlugIn { checkAndPullImages(configs.ImageConfig.Mongodb) setupDatabaseContainer(types.MongoDBGasper) diff --git a/types/constants.go b/types/constants.go index d6b6aa8e..2aebbf16 100644 --- a/types/constants.go +++ b/types/constants.go @@ -48,4 +48,19 @@ const ( // DefaultCPUs is the default number of CPUs allotted to a container DefaultCPUs = 0.25 + + //SeaweedMaster is the master service for Seaweedfs + SeaweedMaster = "seaweed_master" + + //SeaweedVolume is the volume service for Seaweedfs + SeaweedVolume = "seaweed_volume" + + //SeaweedFiler is the filer service for Seaweedfs + SeaweedFiler = "seaweed_filer" + + //SeaweedCronjob is the cronjob service for Seaweedfs + SeaweedCronjob = "seaweed_cronjob" + + //SeaweedS3 is the Seaweed service that provides support for AmazonS3 + SeaweedS3 = "seaweed_s3" ) diff --git a/types/container.go b/types/container.go index 86ce76cb..a334e77d 100644 --- a/types/container.go +++ b/types/container.go @@ -45,6 +45,31 @@ type DatabaseContainer struct { Env M } +// SeaweedfsContainer is the configuration for creating a container +// for running the filesystem +type SeaweedfsContainer struct { + // Name of the container + Name string + // Docker image used for creating the container + Image string + // Port on which a database service is running inside the container + HostPort1 int + // Port on which a database service is running inside the container + HostPort2 int + // Port of the docker container in the host system + ContainerPort1 int + // Port of the docker container in the host system + ContainerPort2 int + // Directory inside the docker container for volume mounting purposes + WorkDir string + // Directory on the host system for volume mounting purposes + StoreDir string + // Custom commands to be executed on a container's startup + Cmd []string + // Environment variables + Env M +} + // HasCustomCMD checks whether a database container needs custom CMD commands on boot func (containerCfg *DatabaseContainer) HasCustomCMD() bool { return len(containerCfg.Cmd) > 0