-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial commit to support custom amis as a new action
- Loading branch information
1 parent
dff250b
commit 9f09ad4
Showing
5 changed files
with
414 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package hosts | ||
|
||
import ( | ||
params "github.com/adrianriobo/qenvs/cmd/cmd/constants" | ||
qenvsContext "github.com/adrianriobo/qenvs/pkg/manager/context" | ||
"github.com/adrianriobo/qenvs/pkg/provider/aws/action/custom" | ||
"github.com/adrianriobo/qenvs/pkg/provider/aws/action/fedora" | ||
"github.com/adrianriobo/qenvs/pkg/util/logging" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/pflag" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
const ( | ||
cmdCustom = "fedora" | ||
cmdCustomDesc = "manage " | ||
|
||
amiID string = "ami" | ||
amiIDDesc string = "ID for the custom ami" | ||
instanceType string = "instance-type" | ||
instanceTypeDesc string = "type of instance" | ||
productDescription string = "product-description" | ||
productDescriptionDesc string = "Product description for the custom AMI: (Linux/UNIX, Windows or Red Hat Enterprise Linux)" | ||
productDescriptionDefault string = "Linux/UNIX" | ||
) | ||
|
||
func GetCustomCmd() *cobra.Command { | ||
c := &cobra.Command{ | ||
Use: cmdCustom, | ||
Short: cmdCustomDesc, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
if err := viper.BindPFlags(cmd.Flags()); err != nil { | ||
return err | ||
} | ||
return nil | ||
}, | ||
} | ||
c.AddCommand(getCustomCreate(), getCustomDestroy()) | ||
return c | ||
} | ||
|
||
func getCustomCreate() *cobra.Command { | ||
c := &cobra.Command{ | ||
Use: params.CreateCmdName, | ||
Short: params.CreateCmdName, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
if err := viper.BindPFlags(cmd.Flags()); err != nil { | ||
return err | ||
} | ||
|
||
// Initialize context | ||
qenvsContext.Init( | ||
viper.GetString(params.ProjectName), | ||
viper.GetString(params.BackedURL), | ||
viper.GetString(params.ConnectionDetailsOutput), | ||
viper.GetStringMapString(params.Tags)) | ||
|
||
// Run create | ||
if err := custom.Create( | ||
&custom.Request{ | ||
Prefix: "main", | ||
AMI: viper.GetString(amiID), | ||
InstanceType: viper.GetString(instanceType), | ||
ProductDescription: viper.GetString(productDescription), | ||
Spot: viper.IsSet(spot), | ||
Airgap: viper.IsSet(airgap)}); err != nil { | ||
logging.Error(err) | ||
} | ||
return nil | ||
}, | ||
} | ||
flagSet := pflag.NewFlagSet(params.CreateCmdName, pflag.ExitOnError) | ||
flagSet.StringP(params.ConnectionDetailsOutput, "", "", params.ConnectionDetailsOutputDesc) | ||
flagSet.StringToStringP(params.Tags, "", nil, params.TagsDesc) | ||
flagSet.StringP(amiID, "", "", amiIDDesc) | ||
flagSet.StringP(instanceType, "", "", instanceTypeDesc) | ||
flagSet.StringP(productDescription, "", productDescriptionDefault, productDescriptionDesc) | ||
flagSet.Bool(airgap, false, airgapDesc) | ||
flagSet.Bool(spot, false, spotDesc) | ||
c.PersistentFlags().AddFlagSet(flagSet) | ||
err := c.MarkPersistentFlagRequired(amiID) | ||
if err != nil { | ||
logging.Error(err) | ||
} | ||
err = c.MarkPersistentFlagRequired(instanceType) | ||
if err != nil { | ||
logging.Error(err) | ||
} | ||
return c | ||
} | ||
|
||
func getCustomDestroy() *cobra.Command { | ||
c := &cobra.Command{ | ||
Use: params.DestroyCmdName, | ||
Short: params.DestroyCmdName, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
if err := viper.BindPFlags(cmd.Flags()); err != nil { | ||
return err | ||
} | ||
|
||
qenvsContext.InitBase( | ||
viper.GetString(params.ProjectName), | ||
viper.GetString(params.BackedURL)) | ||
|
||
if err := fedora.Destroy(); err != nil { | ||
logging.Error(err) | ||
} | ||
return nil | ||
}, | ||
} | ||
return c | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package custom | ||
|
||
var ( | ||
stackName = "stackCustom" | ||
awsCustomID = "ac" | ||
|
||
diskSize int = 200 | ||
|
||
// amiRegex = "Fedora-Cloud-Base-%s*" | ||
// amiOwner = "125523088429" | ||
// amiUserDefault = "fedora" | ||
|
||
// requiredInstanceTypes = []string{"c5.metal", "c5d.metal", "c5n.metal"} | ||
|
||
outputHost = "acdHost" | ||
outputUsername = "acUsername" | ||
outputUserPrivateKey = "acPrivatekey" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
package custom | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/adrianriobo/qenvs/pkg/manager" | ||
qenvsContext "github.com/adrianriobo/qenvs/pkg/manager/context" | ||
infra "github.com/adrianriobo/qenvs/pkg/provider" | ||
"github.com/adrianriobo/qenvs/pkg/provider/aws" | ||
"github.com/adrianriobo/qenvs/pkg/provider/aws/data" | ||
"github.com/adrianriobo/qenvs/pkg/provider/aws/modules/bastion" | ||
"github.com/adrianriobo/qenvs/pkg/provider/aws/modules/ec2/compute" | ||
"github.com/adrianriobo/qenvs/pkg/provider/aws/modules/network" | ||
"github.com/adrianriobo/qenvs/pkg/provider/aws/modules/spot" | ||
amiSVC "github.com/adrianriobo/qenvs/pkg/provider/aws/services/ec2/ami" | ||
"github.com/adrianriobo/qenvs/pkg/provider/aws/services/ec2/keypair" | ||
securityGroup "github.com/adrianriobo/qenvs/pkg/provider/aws/services/ec2/security-group" | ||
"github.com/adrianriobo/qenvs/pkg/provider/util/command" | ||
"github.com/adrianriobo/qenvs/pkg/provider/util/output" | ||
"github.com/adrianriobo/qenvs/pkg/util" | ||
"github.com/adrianriobo/qenvs/pkg/util/logging" | ||
resourcesUtil "github.com/adrianriobo/qenvs/pkg/util/resources" | ||
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ec2" | ||
"github.com/pulumi/pulumi/sdk/v3/go/auto" | ||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi" | ||
) | ||
|
||
type Request struct { | ||
Prefix string | ||
AMI string | ||
InstanceType string | ||
ProductDescription string | ||
Spot bool | ||
Airgap bool | ||
// internal management | ||
// For airgap scenario there is an orchestation of | ||
// a phase with connectivity on the machine (allowing bootstraping) | ||
// a pahase with connectivyt off where the subnet for the target lost the nat gateway | ||
airgapPhaseConnectivity network.Connectivity | ||
// location and price (if Spot is enable) | ||
region string | ||
az string | ||
spotPrice float64 | ||
} | ||
|
||
// Create orchestrate 2 stacks: | ||
// If spot is enable it will run best spot option to get the best option to spin the machine | ||
// Then it will run the stack for windows dedicated host | ||
func Create(r *Request) error { | ||
// if r.Spot { | ||
// sr := spot.SpotOptionRequest{ | ||
// Prefix: r.Prefix, | ||
// ProductDescription: "Linux/UNIX", | ||
// InstaceTypes: , | ||
// AMIName: fmt.Sprintf(amiRegex, r.Version), | ||
// AMIArch: "x86_64", | ||
// } | ||
// so, err := sr.Create() | ||
// if err != nil { | ||
// return err | ||
// } | ||
// r.region = so.Region | ||
// r.az = so.AvailabilityZone | ||
// r.spotPrice = so.MaxPrice | ||
// } else { | ||
// r.region = os.Getenv("AWS_DEFAULT_REGION") | ||
// az, err := data.GetRandomAvailabilityZone(r.region, nil) | ||
// if err != nil { | ||
// return err | ||
// } | ||
// r.az = *az | ||
// } | ||
|
||
// // if not only host the mac machine will be created | ||
// if !r.Airgap { | ||
// return r.createMachine() | ||
// } | ||
// // Airgap scneario requires orchestration | ||
// return r.createAirgapMachine() | ||
sit, err := data.GetSimilarInstaceTypes(r.InstanceType, "") | ||
logging.Debugf("similar types %v", sit) | ||
return err | ||
} | ||
|
||
// Will destroy resources related to machine | ||
func Destroy() (err error) { | ||
if err := aws.DestroyStack( | ||
aws.DestroyStackRequest{ | ||
Stackname: stackName, | ||
}); err != nil { | ||
return err | ||
} | ||
if spot.Exist() { | ||
return spot.Destroy() | ||
} | ||
return nil | ||
} | ||
|
||
func (r *Request) createMachine() error { | ||
cs := manager.Stack{ | ||
StackName: qenvsContext.StackNameByProject(stackName), | ||
ProjectName: qenvsContext.ProjectName(), | ||
BackedURL: qenvsContext.BackedURL(), | ||
ProviderCredentials: aws.GetClouProviderCredentials( | ||
map[string]string{ | ||
aws.CONFIG_AWS_REGION: r.region}), | ||
DeployFunc: r.deploy, | ||
} | ||
|
||
sr, _ := manager.UpStack(cs) | ||
return r.manageResults(sr) | ||
} | ||
|
||
// Abstract this with a stackAirgapHandle receives a fn (connectivty on / off) err executes | ||
// first on then off | ||
func (r *Request) createAirgapMachine() error { | ||
r.airgapPhaseConnectivity = network.ON | ||
err := r.createMachine() | ||
if err != nil { | ||
return nil | ||
} | ||
r.airgapPhaseConnectivity = network.OFF | ||
return r.createMachine() | ||
} | ||
|
||
// function wil all the logic to deploy resources required by windows | ||
// * create AMI Copy if needed | ||
// * networking | ||
// * key | ||
// * security group | ||
// * compute | ||
// * checks | ||
func (r *Request) deploy(ctx *pulumi.Context) error { | ||
// Get AMI | ||
ami, err := amiSVC.GetAMIByName(ctx, | ||
fmt.Sprintf(amiRegex, r.Version), | ||
Check failure on line 136 in pkg/provider/aws/action/custom/custom.go GitHub Actions / build
|
||
amiOwner, | ||
map[string]string{ | ||
"architecture": "x86_64"}) | ||
if err != nil { | ||
return err | ||
} | ||
// Networking | ||
nr := network.NetworkRequest{ | ||
Prefix: r.Prefix, | ||
ID: awsFedoraDedicatedID, | ||
Region: r.region, | ||
AZ: r.az, | ||
// LB is required if we use as which is used for spot feature | ||
CreateLoadBalancer: &r.Spot, | ||
Airgap: r.Airgap, | ||
AirgapPhaseConnectivity: r.airgapPhaseConnectivity, | ||
} | ||
vpc, targetSubnet, _, bastion, lb, err := nr.Network(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
// Create Keypair | ||
kpr := keypair.KeyPairRequest{ | ||
Name: resourcesUtil.GetResourceName( | ||
r.Prefix, awsFedoraDedicatedID, "pk")} | ||
keyResources, err := kpr.Create(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
ctx.Export(fmt.Sprintf("%s-%s", r.Prefix, outputUserPrivateKey), | ||
keyResources.PrivateKey.PrivateKeyPem) | ||
// Security groups | ||
securityGroups, err := r.securityGroups(ctx, vpc) | ||
if err != nil { | ||
return err | ||
} | ||
cr := compute.ComputeRequest{ | ||
Prefix: r.Prefix, | ||
ID: awsFedoraDedicatedID, | ||
VPC: vpc, | ||
Subnet: targetSubnet, | ||
AMI: ami, | ||
KeyResources: keyResources, | ||
SecurityGroups: securityGroups, | ||
InstaceTypes: requiredInstanceTypes, | ||
DiskSize: &diskSize, | ||
Airgap: r.Airgap, | ||
LB: lb, | ||
LBTargetGroups: []int{22}, | ||
Spot: r.Spot} | ||
c, err := cr.NewCompute(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
ctx.Export(fmt.Sprintf("%s-%s", r.Prefix, outputUsername), | ||
pulumi.String(amiUserDefault)) | ||
ctx.Export(fmt.Sprintf("%s-%s", r.Prefix, outputHost), | ||
c.GetHostIP(!r.Airgap)) | ||
return c.Readiness(ctx, command.CommandPing, r.Prefix, awsFedoraDedicatedID, | ||
keyResources.PrivateKey, amiUserDefault, bastion, []pulumi.Resource{}) | ||
} | ||
|
||
// Write exported values in context to files o a selected target folder | ||
func (r *Request) manageResults(stackResult auto.UpResult) error { | ||
results := map[string]string{ | ||
fmt.Sprintf("%s-%s", r.Prefix, outputUsername): "username", | ||
fmt.Sprintf("%s-%s", r.Prefix, outputUserPrivateKey): "id_rsa", | ||
fmt.Sprintf("%s-%s", r.Prefix, outputHost): "host", | ||
} | ||
if r.Airgap { | ||
err := bastion.WriteOutputs(stackResult, r.Prefix, qenvsContext.GetResultsOutputPath()) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return output.Write(stackResult, qenvsContext.GetResultsOutputPath(), results) | ||
} | ||
|
||
// security group for mac machine with ingress rules for ssh and vnc | ||
func (r *Request) securityGroups(ctx *pulumi.Context, | ||
vpc *ec2.Vpc) (pulumi.StringArray, error) { | ||
// ingress for ssh access from 0.0.0.0 | ||
sshIngressRule := securityGroup.SSH_TCP | ||
sshIngressRule.CidrBlocks = infra.NETWORKING_CIDR_ANY_IPV4 | ||
// Create SG with ingress rules | ||
sg, err := securityGroup.SGRequest{ | ||
Name: resourcesUtil.GetResourceName(r.Prefix, awsFedoraDedicatedID, "sg"), | ||
VPC: vpc, | ||
Description: fmt.Sprintf("sg for %s", awsFedoraDedicatedID), | ||
IngressRules: []securityGroup.IngressRules{ | ||
sshIngressRule}, | ||
}.Create(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
// Convert to an array of IDs | ||
sgs := util.ArrayConvert([]*ec2.SecurityGroup{sg.SG}, | ||
func(sg *ec2.SecurityGroup) pulumi.StringInput { | ||
return sg.ID() | ||
}) | ||
return pulumi.StringArray(sgs[:]), nil | ||
} |
Oops, something went wrong.