diff --git a/README.md b/README.md index be597105..5c26a668 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ * [Azure](#azure) * [Oracle Cloud Infrastructure](#oracle-cloud-infrastructure) * [Ionos Cloud](#ionos-cloud) + * [Amazon AWS](#amazon-aws) - [Minecraft Server Versions 📚](#minecraft-server-versions-) - [Minecraft Proxy Versions 📚](#minecraft-proxy-versions-) - [Server Configs 📋](#server-configs-) @@ -63,6 +64,7 @@ ![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) ![Ionos Cloud](https://img.shields.io/badge/ionos--cloud-003D8F?style=for-the-badge&logo=ionos&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) @@ -86,6 +88,7 @@ It is a private side project of me, to learn more about Go, CLI and multi-cloud + Azure (https://azure.microsoft.com/en-us/) + Oracle Cloud Infrastructure (https://www.oracle.com/cloud/) + Ionos Cloud (https://cloud.ionos.de/) ++ Amazon AWS (https://aws.amazon.com/) ### TL;DR 🚀 @@ -276,6 +279,14 @@ export IONOS_PASSWORD=yyy export IONOS_TOKEN= ``` +#### 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. @@ -361,7 +372,7 @@ metadata: name: minecraft-proxy spec: server: - cloud: + cloud: region: size: ssh: "/Users/dirien/Tools/repos/stackit-minecraft/minecraft/ssh/minecraft" @@ -397,7 +408,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 @@ -479,7 +490,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 🗑 @@ -496,7 +507,7 @@ Examples: mincetl delete \ --filename server-do.yaml --id xxx-xxx-xxx-xxx - + Flags: -f, --filename string that contains the configuration for minectl @@ -528,11 +539,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 🆙 @@ -559,7 +570,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 🔌 @@ -615,7 +626,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 📊 @@ -725,6 +736,7 @@ Apache License, Version 2.0 - [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 - Ionos Cloud [#218](https://github.com/dirien/minectl/issues/218) +- [x] New cloud provider - AWS [#210](https://github.com/dirien/minectl/pull/210) - [ ] ... ### Libraries & Tools 🔥 @@ -757,6 +769,7 @@ Apache License, Version 2.0 - https://github.com/oracle/oci-go-sdk - https://github.com/ionos-cloud/sdk-go - https://github.com/AlecAivazis/survey +- https://github.com/aws/aws-sdk-go ### Legal Disclaimer 👮 @@ -768,4 +781,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 f2a020d2..8e6d3f0b 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|ionos") + listCmd.Flags().StringP("provider", "p", "", "The cloud provider - civo|scaleway|do|hetzner|linode|ovh|equinix|gce|vultr|azure|oci|ionos|aws") listCmd.Flags().StringP("region", "r", "", "The region (gce: zone) for your cloud provider - civo|gce") } diff --git a/config/java/server-aws.yaml b/config/java/server-aws.yaml new file mode 100644 index 00000000..2ea96e11 --- /dev/null +++ b/config/java/server-aws.yaml @@ -0,0 +1,71 @@ +apiVersion: ediri.io/minectl/v1alpha1 +kind: MinecraftServer +metadata: + name: minecraft-server +spec: + monitoring: + enabled: false + server: + cloud: aws + region: us-west-1 + size: t3.xlarge + volumeSize: 100 + ssh: /path/to/my/ssh + port: 25565 + minecraft: + java: + openjdk: 16 + xmx: 2G + xms: 2G + rcon: + password: test + port: 25575 + enabled: true + broadcast: true + edition: java + version: 1.17.1 + eula: true + properties: | + level-seed=stackitminecraftrocks + view-distance=10 + enable-jmx-monitoring=false + server-ip= + resource-pack-prompt= + gamemode=survival + allow-nether=true + enable-command-block=false + sync-chunk-writes=true + enable-query=false + op-permission-level=4 + prevent-proxy-connections=false + resource-pack= + entity-broadcast-range-percentage=100 + level-name=world + player-idle-timeout=0 + motd=Scaleway Minecraft + query.port=25565 + force-gamemode=false + rate-limit=0 + hardcore=false + white-list=false + broadcast-console-to-ops=true + pvp=true + spawn-npcs=true + spawn-animals=true + snooper-enabled=true + difficulty=easy + function-permission-level=2 + network-compression-threshold=256 + text-filtering-config= + require-resource-pack=false + spawn-monsters=true + max-tick-time=60000 + enforce-whitelist=false + use-native-transport=true + max-players=100 + resource-pack-sha1= + spawn-protection=16 + online-mode=true + enable-status=true + allow-flight=false + max-world-size=29999984 diff --git a/go.mod b/go.mod index a51a412b..5e32bc21 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,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.55 @@ -19,7 +20,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 @@ -73,9 +75,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.1 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kr/fs v0.1.0 // indirect github.com/mattn/go-colorable v0.1.9 // indirect diff --git a/go.sum b/go.sum index bb9ed182..aef00582 100644 --- a/go.sum +++ b/go.sum @@ -96,6 +96,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= @@ -300,6 +302,10 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/ionos-cloud/sdk-go/v5 v5.1.7 h1:Ri5rnZy+96PIlA+c31myfu/vwoU/gGFWUoMROgsKikI= github.com/ionos-cloud/sdk-go/v5 v5.1.7/go.mod h1:dU3BVZgiBDAKyo41hFk7D5KWHfqyp4YL4rePIAi2JyM= +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= @@ -593,6 +599,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..4d5f2f4d --- /dev/null +++ b/pkg/cloud/aws/aws.go @@ -0,0 +1,456 @@ +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))) + + var secGroups []*string + var groupID *string + if args.MinecraftResource.GetEdition() == "bedrock" || args.MinecraftResource.GetEdition() == "nukkit" || args.MinecraftResource.GetEdition() == "powernukkit" { + groupID, _, err = a.createEC2SecurityGroup("", "udp", args.MinecraftResource.GetPort()) + if err != nil { + return nil, err + } + } else { + groupID, _, err = a.createEC2SecurityGroup("", "tcp", args.MinecraftResource.GetPort()) + if err != nil { + return nil, err + } + if args.MinecraftResource.HasRCON() { + rconID, _, err := a.createEC2SecurityGroup("", "tcp", args.MinecraftResource.GetRCONPort()) + if err != nil { + return nil, err + } + secGroups = append(secGroups, rconID) + } + } + secGroups = append(secGroups, groupID) + if args.MinecraftResource.HasMonitoring() { + promGroupID, _, err := a.createEC2SecurityGroup("", "tcp", 9090) + if err != nil { + return nil, err + } + secGroups = append(secGroups, promGroupID) + } + sshGroupID, _, err := a.createEC2SecurityGroup("", "tcp", 22) + if err != nil { + return nil, err + } + secGroups = append(secGroups, sshGroupID) + + var networkSpec = ec2.InstanceNetworkInterfaceSpecification{ + DeviceIndex: aws.Int64(int64(0)), + AssociatePublicIpAddress: aws.Bool(true), + DeleteOnTermination: aws.Bool(true), + Groups: secGroups, + } + + 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, protocol string, controlPort int) (*string, *string, error) { + 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 + } + + err = a.createEC2SecurityGroupRule(*group.GroupId, protocol, controlPort, controlPort) + if err != nil { + return group.GroupId, &groupName, err + } + + return group.GroupId, &groupName, nil +} + +func (a *Aws) createEC2SecurityGroupRule(groupID, protocol 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(protocol), + 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 4a0e3962..2e8405f3 100644 --- a/pkg/cloud/cloud.go +++ b/pkg/cloud/cloud.go @@ -14,6 +14,7 @@ var cloudProvider = map[string]string{ "azure": "Azure", "oci": "Oracle Cloud Infrastructure", "ionos": "IONOS Cloud", + "aws": "Amazon WebServices", } func GetCloudProviderFullName(cloud string) string { diff --git a/pkg/model/model.go b/pkg/model/model.go index 0e135b76..6fbf2676 100644 --- a/pkg/model/model.go +++ b/pkg/model/model.go @@ -41,11 +41,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 19e262fb..187ea184 100644 --- a/pkg/provisioner/provisioner.go +++ b/pkg/provisioner/provisioner.go @@ -27,16 +27,27 @@ import ( "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 { @@ -245,8 +256,13 @@ func getProvisioner(provider, region string) (automation.Automation, error) { return nil, err } return cloudProvider, nil +<<<<<<< HEAD case "ionos": cloudProvider, err := ionos.NewIONOS(os.Getenv("IONOS_USERNAME"), os.Getenv("IONOS_PASSWORD"), os.Getenv("IONOS_TOKEN")) +======= + case "aws": + cloudProvider, err := aws.NewAWS(region, os.Getenv("AWS_ACCESS_KEY_ID"), os.Getenv("AWS_SECRET_ACCESS_KEY"), os.Getenv("AWS_SESSION_TOKEN")) +>>>>>>> d5873b2 (feat: introduce aws to minectl) if err != nil { return nil, err } diff --git a/pkg/template/template_test.go b/pkg/template/template_test.go index b97e78cf..b6f4829e 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