From 1da016006ed24498e613c17c652aed50018ce5b8 Mon Sep 17 00:00:00 2001 From: Carlos Panato Date: Sun, 26 Sep 2021 15:10:00 +0200 Subject: [PATCH] feat: introduce aws to minectl Signed-off-by: Carlos Panato --- README.md | 33 +- cmd/minectl/list.go | 2 +- go.mod | 6 +- go.sum | 7 + pkg/cloud/aws/aws.go | 430 ++++++++++++++++++ pkg/cloud/cloud.go | 1 + pkg/model/model.go | 2 +- pkg/provisioner/provisioner.go | 38 +- pkg/template/template_test.go | 100 +++- pkg/template/templates/bash/bash.tmpl | 5 +- .../cloud-init/cloud-config.yaml.tmpl | 5 +- 11 files changed, 579 insertions(+), 50 deletions(-) create mode 100644 pkg/cloud/aws/aws.go diff --git a/README.md b/README.md index b3d1964e..bdc2aacc 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ * [Vultr](#vultr) * [Azure](#azure) * [Oracle Cloud Infrastructure](#oracle-cloud-infrastructure) + * [Amazon AWS](#amazon-aws) - [Minecraft Server Versions 📚](#minecraft-server-versions-) - [Minecraft Proxy Versions 📚](#minecraft-proxy-versions-) - [Server Configs 📋](#server-configs-) @@ -59,6 +60,7 @@ ![Vultr](https://img.shields.io/badge/vultr-007BFC?style=for-the-badge&logo=vultr&logoColor=white) ![Microsoft Azure](https://img.shields.io/badge/Microsoft_Azure-0078D4?style=for-the-badge&logo=microsoft-azure&logoColor=white) ![Oracle Cloud Infrastructure](https://img.shields.io/badge/Oracle_Cloud_Infrastructure-F80000?style=for-the-badge&logo=oracle&logoColor=white) +![Amazon AWS](https://img.shields.io/badge/Amazon_AWS-FF9900?style=for-the-badge&logo=amazonaws&logoColor=white) ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/dirien/minectl/Build%20Binary/main?logo=github&style=for-the-badge) ![GitHub](https://img.shields.io/github/license/dirien/minectl?style=for-the-badge) @@ -81,6 +83,7 @@ It is a private side project of me, to learn more about Go, CLI and multi-cloud + Google Compute Engine (GCE) (https://cloud.google.com/compute) + Azure (https://azure.microsoft.com/en-us/) + Oracle Cloud Infrastructure (https://www.oracle.com/cloud/) ++ Amazon AWS (https://aws.amazon.com/) ### TL;DR 🚀 @@ -249,6 +252,14 @@ region= Please follow the instructions under -> https://docs.oracle.com/en-us/iaas/Content/API/Concepts/apisigningkey.htm +#### Amazon AWS + +```bash +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_REGION= +``` + #### Minecraft Server Versions 📚 > ⚠️ `minectl 🗺` is not(!) providing any pre-compiled binaries of Minecraft or download a pre-compiled version. @@ -334,7 +345,7 @@ metadata: name: minecraft-proxy spec: server: - cloud: + cloud: region: size: ssh: "/Users/dirien/Tools/repos/stackit-minecraft/minecraft/ssh/minecraft" @@ -370,7 +381,7 @@ spec: monitoring: enabled: true|false server: - cloud: "provider: civo|scaleway|do|hetzner|linode|ovh|equinix|gce|vultr|azure|oci" + cloud: "provider: civo|scaleway|do|hetzner|linode|ovh|equinix|gce|vultr|azure|oci|aws" region: "region see cloud provider for details eg. fra1" size: "see cloud provider docs for details eg. g3.large" volumeSize: 100 @@ -429,7 +440,7 @@ Flags: Global Flags: --headless Set this value to if mincetl is called by a CI system. Enables logging and disables human-readable output rendering (default: false) --log-encoding string Set the log encoding: console|json (default: console) (default "console") - --verbose string Enable verbose logging: debug|info|warn|error|dpanic|panic|fatal + --verbose string Enable verbose logging: debug|info|warn|error|dpanic|panic|fatal ``` #### Delete Minecraft Server 🗑 @@ -446,7 +457,7 @@ Examples: mincetl delete \ --filename server-do.yaml --id xxx-xxx-xxx-xxx - + Flags: -f, --filename string that contains the configuration for minectl @@ -478,11 +489,11 @@ Flags: -h, --help help for list -p, --provider string The cloud provider - civo|scaleway|do|hetzner|linode|ovh|equinix|gce|vultr|azure|oci -r, --region string The region for your cloud provider - + Global Flags: --headless Set this value to if mincetl is called by a CI system. Enables logging and disables human-readable output rendering (default: false) --log-encoding string Set the log encoding: console|json (default: console) (default "console") - --verbose string Enable verbose logging: debug|info|warn|error|dpanic|panic|fatal + --verbose string Enable verbose logging: debug|info|warn|error|dpanic|panic|fatal ``` #### Update Minecraft Server 🆙 @@ -509,7 +520,7 @@ Flags: Global Flags: --headless Set this value to if mincetl is called by a CI system. Enables logging and disables human-readable output rendering (default: false) --log-encoding string Set the log encoding: console|json (default: console) (default "console") - --verbose string Enable verbose logging: debug|info|warn|error|dpanic|panic|fatal + --verbose string Enable verbose logging: debug|info|warn|error|dpanic|panic|fatal ``` #### RCON Minecraft Server 🔌 @@ -565,7 +576,7 @@ Flags: Global Flags: --headless Set this value to if mincetl is called by a CI system. Enables logging and disables human-readable output rendering (default: false) --log-encoding string Set the log encoding: console|json (default: console) (default "console") - --verbose string Enable verbose logging: debug|info|warn|error|dpanic|panic|fatal + --verbose string Enable verbose logging: debug|info|warn|error|dpanic|panic|fatal ``` #### Monitoring 📊 @@ -673,7 +684,8 @@ Apache License, Version 2.0 - [x] New cloud provider - Vultr [#90](https://github.com/dirien/minectl/issues/90) - [x] Add Suport for Proxy Server - bungeecord and waterfall [#95](https://github.com/dirien/minectl/issues/95) - [x] New cloud provider - Azure [#56](https://github.com/dirien/minectl/issues/56) -- [x] New cloud provider - Oracle/OCI [#107](https://github.com/dirien/minectl/issues/107) +- [x] New cloud provider - Oracle/OCI [#107](https://github.com/dirien/minectl/issues/107) +- [x] New cloud provider - AWS [#210](https://github.com/dirien/minectl/pull/210) - [ ] ... ### Libraries & Tools 🔥 @@ -704,6 +716,7 @@ Apache License, Version 2.0 - https://github.com/tcnksm/go-latest - https://github.com/uber-go/zap - https://github.com/oracle/oci-go-sdk +- https:/github.com/aws/aws-sdk-go ### Legal Disclaimer 👮 @@ -715,4 +728,4 @@ Other trademarks referenced herein are property of their respective owners. Source: -##### [1] https://www.spigotmc.org/wiki/what-is-spigot-craftbukkit-bukkit-vanilla-forg/ \ No newline at end of file +##### [1] https://www.spigotmc.org/wiki/what-is-spigot-craftbukkit-bukkit-vanilla-forg/ diff --git a/cmd/minectl/list.go b/cmd/minectl/list.go index b58725ae..d9983b38 100644 --- a/cmd/minectl/list.go +++ b/cmd/minectl/list.go @@ -12,7 +12,7 @@ import ( func init() { - listCmd.Flags().StringP("provider", "p", "", "The cloud provider - civo|scaleway|do|hetzner|linode|ovh|equinix|gce|vultr|azure|oci") + listCmd.Flags().StringP("provider", "p", "", "The cloud provider - civo|scaleway|do|hetzner|linode|ovh|equinix|gce|vultr|azure|oci|aws") listCmd.Flags().StringP("region", "r", "", "The region (gce: zone) for your cloud provider - civo|gce") } diff --git a/go.mod b/go.mod index a8519adb..6013c3a6 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 github.com/Tnze/go-mc v1.17.0 + github.com/aws/aws-sdk-go v1.40.15 github.com/blang/semver/v4 v4.0.0 github.com/c-bata/go-prompt v0.2.6 github.com/civo/civogo v0.2.54 @@ -18,7 +19,8 @@ require ( github.com/fatih/color v1.13.0 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-github v17.0.0+incompatible // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/google/uuid v1.3.0 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.0 github.com/hashicorp/go-version v1.3.0 // indirect @@ -70,9 +72,9 @@ require ( github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-querystring v1.1.0 // indirect github.com/googleapis/gax-go/v2 v2.1.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kr/fs v0.1.0 // indirect github.com/mattn/go-colorable v0.1.9 // indirect github.com/mattn/go-tty v0.0.3 // indirect diff --git a/go.sum b/go.sum index 0bfb0a3e..1ea2e08e 100644 --- a/go.sum +++ b/go.sum @@ -92,6 +92,8 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go v1.40.15 h1:aqQCwW8meVzLCacWX8NEPg8bBkL0ZlcMSbhwrsg6eNE= +github.com/aws/aws-sdk-go v1.40.15/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -291,6 +293,10 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -575,6 +581,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= diff --git a/pkg/cloud/aws/aws.go b/pkg/cloud/aws/aws.go new file mode 100644 index 00000000..4c6a546f --- /dev/null +++ b/pkg/cloud/aws/aws.go @@ -0,0 +1,430 @@ +package aws + +import ( + "context" + _ "embed" + "encoding/base64" + "fmt" + "io/ioutil" + "path/filepath" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/google/uuid" + "github.com/minectl/pkg/automation" + "github.com/minectl/pkg/common" + minctlTemplate "github.com/minectl/pkg/template" + "github.com/minectl/pkg/update" + "github.com/pkg/errors" +) + +type Aws struct { + client *ec2.EC2 + tmpl *minctlTemplate.Template + region string +} + +// NewAWS creates an Aws and initialises an EC2 client +func NewAWS(region, accessKey, secretKey, token string) (*Aws, error) { + sess, err := session.NewSession(&aws.Config{ + Region: aws.String(region), + Credentials: credentials.NewStaticCredentials(accessKey, secretKey, token), + }) + if err != nil { + return nil, err + } + + ec2Svc := ec2.New(sess) + + tmpl, err := minctlTemplate.NewTemplateCloudConfig() + if err != nil { + return nil, err + } + + return &Aws{ + client: ec2Svc, + region: region, + tmpl: tmpl, + }, err +} + +func (a *Aws) ListServer() ([]automation.RessourceResults, error) { + var result []automation.RessourceResults + var nextToken *string + + for { + input := &ec2.DescribeInstancesInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String(fmt.Sprintf("tag:%s", common.InstanceTag)), + Values: []*string{aws.String("true")}, + }, + }, + NextToken: nextToken, + } + + instances, err := a.client.DescribeInstances(input) + if err != nil { + return nil, err + } + + for _, r := range instances.Reservations { + for _, i := range r.Instances { + if *i.State.Name != ec2.InstanceStateNameTerminated { + arr := automation.RessourceResults{ + ID: *i.InstanceId, + Region: a.region, + } + + if i.PublicIpAddress != nil { + arr.PublicIP = *i.PublicIpAddress + } + + var tags []string + var instanceName string + for _, v := range i.Tags { + tags = append(tags, fmt.Sprintf("%s=%s", *v.Key, *v.Value)) + + if *v.Key == "Name" { + instanceName = *v.Value + } + } + + arr.Tags = strings.Join(tags, ",") + arr.Name = instanceName + + result = append(result, arr) + } + } + } + + nextToken = instances.NextToken + if nextToken == nil { + break + } + } + + return result, nil +} + +func (a *Aws) CreateServer(args automation.ServerArgs) (*automation.RessourceResults, error) { + image, err := a.lookupAMI("ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20210621") + if err != nil { + return nil, err + } + + pubKeyFile, err := ioutil.ReadFile(fmt.Sprintf("%s.pub", args.MinecraftResource.GetSSH())) + if err != nil { + return nil, err + } + + key, err := a.client.ImportKeyPair(&ec2.ImportKeyPairInput{ + KeyName: aws.String(fmt.Sprintf("%s-ssh", args.MinecraftResource.GetName())), + PublicKeyMaterial: pubKeyFile, + }) + if err != nil { + return nil, err + } + + instanceInput := &ec2.RunInstancesInput{ + ImageId: image, + KeyName: key.KeyName, + InstanceType: aws.String(args.MinecraftResource.GetSize()), + MinCount: aws.Int64(1), + MaxCount: aws.Int64(1), + TagSpecifications: []*ec2.TagSpecification{ + { + ResourceType: aws.String("instance"), + Tags: []*ec2.Tag{ + { + Key: aws.String("Name"), + Value: aws.String(args.MinecraftResource.GetName()), + }, + { + Key: aws.String(common.InstanceTag), + Value: aws.String("true"), + }, + }, + }, + }, + } + + if args.MinecraftResource.GetVolumeSize() > 0 { + instanceInput.BlockDeviceMappings = []*ec2.BlockDeviceMapping{ + { + DeviceName: aws.String("/dev/sda1"), + Ebs: &ec2.EbsBlockDevice{ + VolumeSize: aws.Int64(int64(args.MinecraftResource.Spec.Server.VolumeSize)), + }, + }, + } + } + + userData, err := a.tmpl.GetTemplate(args.MinecraftResource, "", minctlTemplate.GetTemplateCloudConfigName(args.MinecraftResource.IsProxyServer())) + if err != nil { + return nil, err + } + instanceInput.UserData = aws.String(base64.StdEncoding.EncodeToString([]byte(userData))) + + groupID, _, err := a.createEC2SecurityGroup("", args.MinecraftResource.GetPort()) + if err != nil { + return nil, err + } + + var networkSpec = ec2.InstanceNetworkInterfaceSpecification{ + DeviceIndex: aws.Int64(int64(0)), + AssociatePublicIpAddress: aws.Bool(true), + DeleteOnTermination: aws.Bool(true), + Groups: []*string{groupID}, + } + + instanceInput.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{ + &networkSpec, + } + + result, err := a.client.RunInstances(instanceInput) + if err != nil { + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + default: + return nil, aerr + } + } else { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) + defer cancel() + for { + select { + case <-ctx.Done(): + return nil, errors.New("timed out while creating the aws instance") + case <-time.After(10 * time.Second): + instanceStatus, err := a.client.DescribeInstanceStatus(&ec2.DescribeInstanceStatusInput{ + InstanceIds: aws.StringSlice([]string{*result.Instances[0].InstanceId}), + }) + if err != nil { + return nil, err + } + if *instanceStatus.InstanceStatuses[0].InstanceState.Name == "running" { + i, err := a.client.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIds: aws.StringSlice([]string{*result.Instances[0].InstanceId}), + }) + if err != nil { + return nil, err + } + var tags []string + var instanceName string + for _, v := range i.Reservations[0].Instances[0].Tags { + tags = append(tags, fmt.Sprintf("%s=%s", *v.Key, *v.Value)) + + if *v.Key == "Name" { + instanceName = *v.Value + } + } + + return &automation.RessourceResults{ + ID: *i.Reservations[0].Instances[0].InstanceId, + Name: instanceName, + Region: *a.client.Config.Region, + PublicIP: *i.Reservations[0].Instances[0].PublicIpAddress, + Tags: strings.Join(tags, ","), + }, nil + } + } + } +} + +func (a *Aws) UpdateServer(id string, args automation.ServerArgs) error { + i, err := a.client.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIds: aws.StringSlice([]string{id}), + }) + if err != nil { + return err + } + + remoteCommand := update.NewRemoteServer(args.MinecraftResource.GetSSH(), *i.Reservations[0].Instances[0].PublicIpAddress, "ubuntu") + err = remoteCommand.UpdateServer(args.MinecraftResource) + if err != nil { + return err + } + + return nil +} + +func (a *Aws) DeleteServer(id string, args automation.ServerArgs) error { + keys, err := a.client.DescribeKeyPairs(&ec2.DescribeKeyPairsInput{ + KeyNames: aws.StringSlice([]string{fmt.Sprintf("%s-ssh", args.MinecraftResource.GetName())}), + }) + if err != nil { + return err + } + + _, err = a.client.DeleteKeyPair(&ec2.DeleteKeyPairInput{ + KeyName: aws.String(*keys.KeyPairs[0].KeyName), + }) + if err != nil { + return err + } + + results, err := a.ListServer() + if err != nil { + return err + } + + for _, result := range results { + i, err := a.client.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIds: aws.StringSlice([]string{result.ID}), + }) + if err != nil { + return err + } + groups := i.Reservations[0].Instances[0].SecurityGroups + + _, err = a.client.TerminateInstances(&ec2.TerminateInstancesInput{ + InstanceIds: aws.StringSlice([]string{result.ID}), + }) + if err != nil { + return err + } + + err = a.client.WaitUntilInstanceTerminated(&ec2.DescribeInstancesInput{ + InstanceIds: aws.StringSlice([]string{result.ID}), + }) + if err != nil { + return err + } + + for _, group := range groups { + _, err := a.client.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{ + GroupId: group.GroupId, + }) + if err != nil { + return err + } + } + } + + return nil +} + +func (a *Aws) UploadPlugin(id string, args automation.ServerArgs, plugin, destination string) error { + i, err := a.client.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIds: aws.StringSlice([]string{id}), + }) + if err != nil { + return err + } + + remoteCommand := update.NewRemoteServer(args.MinecraftResource.GetSSH(), *i.Reservations[0].Instances[0].PublicIpAddress, "ubuntu") + + err = remoteCommand.TransferFile(plugin, filepath.Join(destination, filepath.Base(plugin))) + if err != nil { + return err + } + + _, err = remoteCommand.ExecuteCommand("systemctl restart minecraft.service") + if err != nil { + return err + } + + return nil +} + +func (a *Aws) GetServer(id string, _ automation.ServerArgs) (*automation.RessourceResults, error) { + i, err := a.client.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIds: aws.StringSlice([]string{id}), + }) + if err != nil { + return nil, err + } + + var tags []string + var instanceName string + for _, v := range i.Reservations[0].Instances[0].Tags { + tags = append(tags, fmt.Sprintf("%s=%s", *v.Key, *v.Value)) + + if *v.Key == "Name" { + instanceName = *v.Value + } + } + + return &automation.RessourceResults{ + ID: *i.Reservations[0].Instances[0].InstanceId, + Name: instanceName, + Region: *a.client.Config.Region, + PublicIP: *i.Reservations[0].Instances[0].PublicIpAddress, + Tags: strings.Join(tags, ","), + }, err +} + +func (a *Aws) createEC2SecurityGroup(vpcID string, controlPort int) (*string, *string, error) { + ports := []int{80, 443, 22, controlPort} + groupName := "minecraft-" + uuid.New().String() + var input = &ec2.CreateSecurityGroupInput{ + Description: aws.String("minecraft security group"), + GroupName: aws.String(groupName), + } + + if len(vpcID) > 0 { + input.VpcId = aws.String(vpcID) + } + + group, err := a.client.CreateSecurityGroup(input) + if err != nil { + return nil, nil, err + } + + for _, port := range ports { + err = a.createEC2SecurityGroupRule(*group.GroupId, port, port) + if err != nil { + return group.GroupId, &groupName, err + } + } + + return group.GroupId, &groupName, nil +} + +func (a *Aws) createEC2SecurityGroupRule(groupID string, fromPort, toPort int) error { + _, err := a.client.AuthorizeSecurityGroupIngress(&ec2.AuthorizeSecurityGroupIngressInput{ + CidrIp: aws.String("0.0.0.0/0"), + FromPort: aws.Int64(int64(fromPort)), + IpProtocol: aws.String("tcp"), + ToPort: aws.Int64(int64(toPort)), + GroupId: aws.String(groupID), + }) + if err != nil { + return err + } + + return nil +} + +// lookupAMI gets the AMI ID that the exit node will use +func (a *Aws) lookupAMI(name string) (*string, error) { + images, err := a.client.DescribeImages(&ec2.DescribeImagesInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("name"), + Values: []*string{ + aws.String(name), + }, + }, + }, + }) + if err != nil { + return nil, err + } + + if len(images.Images) == 0 { + return nil, fmt.Errorf("image not found") + } + + return images.Images[0].ImageId, nil +} diff --git a/pkg/cloud/cloud.go b/pkg/cloud/cloud.go index e24be711..f6ef1246 100644 --- a/pkg/cloud/cloud.go +++ b/pkg/cloud/cloud.go @@ -13,6 +13,7 @@ var cloudProvider = map[string]string{ "vultr": "vultr", "azure": "Azure", "oci": "Oracle Cloud Infrastructure", + "aws": "Amazon WebServices", } func GetCloudProviderFullName(cloud string) string { diff --git a/pkg/model/model.go b/pkg/model/model.go index 3d424b6e..d3445e1b 100644 --- a/pkg/model/model.go +++ b/pkg/model/model.go @@ -25,11 +25,11 @@ type Monitoring struct { // Server type Server struct { Size string `yaml:"size"` - VolumeSize int `yaml:"volumeSize"` Ssh string `yaml:"ssh"` Cloud string `yaml:"cloud"` Region string `yaml:"region"` Port int `yaml:"port"` + VolumeSize int `yaml:"volumeSize"` } // Minecraft diff --git a/pkg/provisioner/provisioner.go b/pkg/provisioner/provisioner.go index eb41cc6a..e786691d 100644 --- a/pkg/provisioner/provisioner.go +++ b/pkg/provisioner/provisioner.go @@ -6,35 +6,27 @@ import ( "os" "time" - "github.com/minectl/pkg/cloud/oci" - - "github.com/minectl/pkg/progress" - - "github.com/minectl/pkg/logging" - - "github.com/minectl/pkg/cloud/azure" - - "github.com/minectl/pkg/rcon" - - "github.com/minectl/pkg/cloud/vultr" - - "github.com/minectl/pkg/cloud/gce" - - "github.com/minectl/pkg/cloud/equinix" - - "github.com/minectl/pkg/cloud/ovh" - - "github.com/minectl/pkg/cloud/linode" + "github.com/pkg/errors" "github.com/minectl/pkg/automation" "github.com/minectl/pkg/cloud" + "github.com/minectl/pkg/cloud/aws" + "github.com/minectl/pkg/cloud/azure" "github.com/minectl/pkg/cloud/civo" "github.com/minectl/pkg/cloud/do" + "github.com/minectl/pkg/cloud/equinix" + "github.com/minectl/pkg/cloud/gce" "github.com/minectl/pkg/cloud/hetzner" + "github.com/minectl/pkg/cloud/linode" + "github.com/minectl/pkg/cloud/oci" + "github.com/minectl/pkg/cloud/ovh" "github.com/minectl/pkg/cloud/scaleway" + "github.com/minectl/pkg/cloud/vultr" "github.com/minectl/pkg/common" + "github.com/minectl/pkg/logging" "github.com/minectl/pkg/manifest" - "github.com/pkg/errors" + "github.com/minectl/pkg/progress" + "github.com/minectl/pkg/rcon" ) type MinectlProvisionerOpts struct { @@ -243,6 +235,12 @@ func getProvisioner(provider, region string) (automation.Automation, error) { return nil, err } return cloudProvider, nil + case "aws": + cloudProvider, err := aws.NewAWS(region, os.Getenv("AWS_ACCESS_KEY_ID"), os.Getenv("AWS_SECRET_ACCESS_KEY"), os.Getenv("AWS_SESSION_TOKEN")) + if err != nil { + return nil, err + } + return cloudProvider, nil default: return nil, errors.Errorf("Could not find provider %s", provider) } diff --git a/pkg/template/template_test.go b/pkg/template/template_test.go index 39e9f2fa..a8276626 100644 --- a/pkg/template/template_test.go +++ b/pkg/template/template_test.go @@ -376,6 +376,8 @@ tee /etc/systemd/system/minecraft.service < /tmp/bedrock-server.zip unzip -o /tmp/bedrock-server.zip -d /minecraft chmod +x /minecraft/bedrock_server echo "eula=false" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -527,6 +530,8 @@ tee /etc/systemd/system/minecraft.service < /minecraft/server.jar echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -688,6 +694,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -742,6 +750,7 @@ runcmd: - chmod +x /minecraft/bedrock_server - echo "eula=false" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service` @@ -853,6 +862,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -912,6 +923,7 @@ runcmd: - curl -sLSf $URL > /minecraft/server.jar - echo "eula=true" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service` @@ -1023,6 +1035,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -1082,6 +1096,7 @@ runcmd: - curl -sLSf $URL > /minecraft/server.jar - echo "eula=true" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service` @@ -1146,6 +1161,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -1213,6 +1230,7 @@ unzip -o /tmp/bedrock-server.zip -d /minecraft chmod +x /minecraft/bedrock_server echo "eula=false" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -1300,6 +1318,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -1375,6 +1395,7 @@ URL=$(curl -s https://java-version.minectl.ediri.online/binary/1.17) curl -sLSf $URL > /minecraft/server.jar echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -1462,6 +1483,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -1537,6 +1560,7 @@ URL=$(curl -s https://java-version.minectl.ediri.online/binary/1.17) curl -sLSf $URL > /minecraft/server.jar echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -1624,6 +1648,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -1699,6 +1725,7 @@ URL="https://papermc.io/api/v2/projects/paper/versions/1.17.1/builds/157/downloa curl -sLSf $URL > /minecraft/server.jar echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -1810,6 +1837,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -1879,6 +1908,7 @@ runcmd: - rm -rf /tmp/build - echo "eula=true" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service` @@ -1966,6 +1996,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -2048,6 +2080,7 @@ cp craftbukkit-1.17.1-138.jar /minecraft/server.jar rm -rf /tmp/build echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -2159,6 +2192,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -2225,6 +2260,7 @@ runcmd: - rm -rf /tmp/build - echo "eula=true" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service` @@ -2312,6 +2348,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -2394,6 +2432,7 @@ cp /tmp/build/server.jar /minecraft/minecraft-server.jar rm -rf /tmp/build echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -2505,6 +2544,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -2570,6 +2611,7 @@ runcmd: - rm -rf /tmp/build - echo "eula=true" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service` @@ -2657,6 +2699,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -2738,6 +2782,7 @@ cp /minecraft/forge-1.17.1-138.jar /minecraft/server.jar rm -rf /tmp/build echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -2849,6 +2894,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -2918,6 +2965,7 @@ runcmd: - rm -rf /tmp/build - echo "eula=true" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service` @@ -3005,6 +3053,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -3087,6 +3137,7 @@ cp spigot-1.17.1-138.jar /minecraft/server.jar rm -rf /tmp/build echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -3103,6 +3154,8 @@ tee /etc/systemd/system/minecraft.service < /tmp/bedrock-server.zip unzip -o /tmp/bedrock-server.zip -d /minecraft chmod +x /minecraft/bedrock_server echo "eula=false" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -3174,6 +3228,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -3205,6 +3261,7 @@ runcmd: - rm -rf /tmp/build - echo "eula=true" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service` @@ -3225,6 +3282,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -3265,6 +3324,7 @@ cp /tmp/build/server.jar /minecraft/minecraft-server.jar rm -rf /tmp/build echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -3285,6 +3345,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -3316,6 +3378,7 @@ URL="https://ci.opencollab.dev/job/NukkitX/job/Nukkit/job/master/lastSuccessfulB curl -sLSf $URL > /minecraft/server.jar echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -3358,6 +3421,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -3382,6 +3447,7 @@ runcmd: - curl -sLSf $URL > /minecraft/server.jar - echo "eula=true" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service` @@ -3402,6 +3468,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fstab @@ -3433,6 +3501,7 @@ URL="https://github.com/PowerNukkit/PowerNukkit/releases/download/v1.5.1.0-PN/po curl -sLSf $URL > /minecraft/server.jar echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service` @@ -3475,6 +3544,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -3499,6 +3570,7 @@ runcmd: - curl -sLSf $URL > /minecraft/server.jar - echo "eula=true" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service` ) diff --git a/pkg/template/templates/bash/bash.tmpl b/pkg/template/templates/bash/bash.tmpl index 730abb08..73a3c56f 100644 --- a/pkg/template/templates/bash/bash.tmpl +++ b/pkg/template/templates/bash/bash.tmpl @@ -19,6 +19,8 @@ tee /etc/systemd/system/minecraft.service <> /etc/fst {{- end }} echo "eula={{ .Spec.Minecraft.Eula }}" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties +chmod a+rwx /minecraft systemctl restart minecraft.service systemctl enable minecraft.service {{- end -}} \ No newline at end of file diff --git a/pkg/template/templates/cloud-init/cloud-config.yaml.tmpl b/pkg/template/templates/cloud-init/cloud-config.yaml.tmpl index e79c0bf2..8c102d16 100644 --- a/pkg/template/templates/cloud-init/cloud-config.yaml.tmpl +++ b/pkg/template/templates/cloud-init/cloud-config.yaml.tmpl @@ -55,6 +55,8 @@ write_files: [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server + DefaultDependencies=no + After=network.target [Service] WorkingDirectory=/minecraft Type=simple @@ -74,7 +76,7 @@ runcmd: {{- template "monitoring-binaries" . }} {{- end }} {{- if not .Mount }} - - mkdir /minecraft + - mkdir -p /minecraft {{- end }} - ufw allow ssh - ufw allow 5201 @@ -107,6 +109,7 @@ runcmd: {{- end }} - echo "eula={{ .Spec.Minecraft.Eula }}" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties + - chmod a+rwx /minecraft - systemctl restart minecraft.service - systemctl enable minecraft.service {{- end -}} \ No newline at end of file