Skip to content

Commit

Permalink
chore: latest changes for update user password on replace
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Riobo Lorenzo <[email protected]>
  • Loading branch information
adrianriobo committed Feb 1, 2024
1 parent b65a313 commit 6896c8e
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 97 deletions.
14 changes: 12 additions & 2 deletions pkg/provider/aws/action/mac/bootstrap.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
#!/bin/sh

# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-mac-instances.html
# Internal disk will be accessible on workspace dir
# mkdir -p "/Users/{{.Username}}/workspace"
echo 'if [[ ! -d /Volumes/InternalDisk ]]; then' | tee -a "/Users/{{.Username}}/.zshrc"
echo 'APFSVolumeName="InternalDisk" ; SSDContainer=$(diskutil list | grep "Physical Store disk0" -B 3 | grep "/dev/disk" | awk {"print $1"} ) ; diskutil apfs addVolume $SSDContainer APFS $APFSVolumeName' | tee -a "/Users/{{.Username}}/.zshrc"
echo 'sudo chown {{.Username}}:staff "/Volumes/InternalDisk"' | tee -a "/Users/{{.Username}}/.zshrc"
echo 'sudo chmod 0750 "/Volumes/InternalDisk"' | tee -a "/Users/{{.Username}}/.zshrc"
echo 'fi' | tee -a "/Users/{{.Username}}/.zshrc"

# Allow run x86 binaries on arm64
# TODO review if still required for CrC
sudo softwareupdate --install-rosetta --agree-to-license

# Enable remote control (vnc)
Expand All @@ -18,12 +28,12 @@ sudo defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser "{{

sudo defaults write /Library/Preferences/.GlobalPreferences.plist com.apple.securitypref.logoutvalue -int 0
sudo defaults write /Library/Preferences/.GlobalPreferences.plist com.apple.autologout.AutoLogOutDelay -int 0
sysadminctl -screenLock off -password "{{.Password}}"

# Override authorized key
mkdir /Users/{{.Username}}/.ssh
echo "{{.AuthorizedKey}}" | tee /Users/{{.Username}}/.ssh/authorized_keys

# TODO need to change the authorized key

# autologin to take effect
# run reboot on background to successfully finish the remote exec of the script
(sleep 2 && sudo reboot)&
9 changes: 2 additions & 7 deletions pkg/provider/aws/action/mac/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,14 @@ const (
outputDedicatedHostID = "ammDedicatedHostID"
outputDedicatedHostAZ = "ammDedicatedHostAZ"
outputRegion = "ammRegion"
// outputAdminUsername = "ammAdminUsername"
// outputAdminUserPassword = "ammAdminUserPassword"

// outputDHBackedURL = "ammDHBackedURL"
// outputDHProjectName = "ammDHProjectName"

amiRegex = "amzn-ec2-macos-%s*"
// amiOwner = "628277914472"
amiRegex = "amzn-ec2-macos-%s*"
archDefault = "m2"
osVersionDefault = "14"

vncDefaultPort int = 5900
diskSize int = 100
blockDeviceType string = "gp3"
defaultUsername string = "ec2-user"
defaultSSHPort int = 22

Expand Down
2 changes: 1 addition & 1 deletion pkg/provider/aws/action/mac/mac-dh.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (r *MacRequest) createDedicatedHost() (dhi *HostInformation, err error) {
return nil, err
}
i := getHostInformation(*host)
dhi = &i
dhi = i
return
}

Expand Down
72 changes: 35 additions & 37 deletions pkg/provider/aws/action/mac/mac-machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"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/network"
"github.com/adrianriobo/qenvs/pkg/provider/aws/services/ec2/ami"
qEC2 "github.com/adrianriobo/qenvs/pkg/provider/aws/services/ec2/compute"
"github.com/adrianriobo/qenvs/pkg/provider/aws/services/ec2/keypair"
securityGroup "github.com/adrianriobo/qenvs/pkg/provider/aws/services/ec2/security-group"
Expand Down Expand Up @@ -46,7 +45,7 @@ type locked struct {
Lock bool
}

func isMachineLocked(prefix string, h HostInformation) (bool, error) {
func isMachineLocked(prefix string, h *HostInformation) (bool, error) {
s, err := manager.CheckStack(manager.Stack{
StackName: qenvsContext.StackNameByProject(stackMacMachine),
ProjectName: qenvsContext.ProjectName(),
Expand All @@ -68,20 +67,15 @@ func isMachineLocked(prefix string, h HostInformation) (bool, error) {
// This function will use the information from the
// dedicated host holding the mac machine will check if stack exists
// if exists will get the lock value from it
func (r *MacRequest) replaceMachine(h HostInformation) error {
machineLocked, err := isMachineLocked(r.Prefix, h)
if err != nil {
return err
}
if machineLocked {
return fmt.Errorf("can not replace machine is currently locked")
}
r.lock = true
if err := r.manageMacMachine(h); err != nil {
return err
}
func (r *MacRequest) replaceMachine(h *HostInformation) error {
aN := fmt.Sprintf(amiRegex, r.Version)
ami, err := data.GetAMI(&aN, h.Arch, h.Region, nil)
bdt := blockDeviceType
ami, err := data.GetAMI(
data.ImageRequest{
Name: &aN,
Arch: h.Arch,
Region: h.Region,
BlockDeviceType: &bdt})
if err != nil {
return err
}
Expand All @@ -96,14 +90,18 @@ func (r *MacRequest) replaceMachine(h HostInformation) error {
if err != nil {
return err
}
r.lock = true
if err := r.manageMacMachine(h); err != nil {
return err
}
// replace will run again the boostrap script to generate
// and set new keys to access the machine
r.replace = true
return r.manageMacMachine(h)
}

// Release will set the lock as false
func (r *MacRequest) releaseLock(h HostInformation) error {
func (r *MacRequest) releaseLock(h *HostInformation) error {
r.lock = false
lockURN := fmt.Sprintf("urn:pulumi:%s::%s::%s::%s",
qenvsContext.StackNameByProject(stackMacMachine),
Expand All @@ -117,20 +115,20 @@ func (r *MacRequest) releaseLock(h HostInformation) error {
}

// Release will set the lock as false
func (r *MacRequest) createMacMachine(h HostInformation) error {
func (r *MacRequest) createMacMachine(h *HostInformation) error {
r.lock = true
return r.manageMacMachine(h)
}

// this creates the stack for the mac machine
func (r *MacRequest) manageMacMachine(h HostInformation) error {
func (r *MacRequest) manageMacMachine(h *HostInformation) error {
return r.manageMacMachineTargets(h, nil)
}

// this creates the stack for the mac machine
func (r *MacRequest) manageMacMachineTargets(h HostInformation, targetURNs []string) error {
func (r *MacRequest) manageMacMachineTargets(h *HostInformation, targetURNs []string) error {
r.AvailabilityZone = h.Host.AvailabilityZone
r.dedicatedHost = &h
r.dedicatedHost = h
r.Region = h.Region
cs := manager.Stack{
StackName: fmt.Sprintf("%s-%s",
Expand All @@ -152,7 +150,7 @@ func (r *MacRequest) manageMacMachineTargets(h HostInformation, targetURNs []str
}

// this creates the stack for the mac machine
func (r *MacRequest) createAirgapMacMachine(h HostInformation) error {
func (r *MacRequest) createAirgapMacMachine(h *HostInformation) error {
r.airgapPhaseConnectivity = network.ON
err := r.createMacMachine(h)
if err != nil {
Expand All @@ -168,11 +166,15 @@ func (r *MacRequest) deployerMachine(ctx *pulumi.Context) error {
ctx.Export(fmt.Sprintf("%s-%s", r.Prefix, outputRegion), pulumi.String(*r.Region))
ctx.Export(fmt.Sprintf("%s-%s", r.Prefix, outputDedicatedHostID), pulumi.String(*r.dedicatedHost.Host.HostId))
// Lookup AMI
ami, err := ami.GetAMIByName(ctx,
fmt.Sprintf(amiRegex, r.Version),
"",
map[string]string{
"architecture": awsArchIDbyArch[r.Architecture]})
aN := fmt.Sprintf(amiRegex, r.Version)
bdt := blockDeviceType
arch := awsArchIDbyArch[r.Architecture]
ami, err := data.GetAMI(
data.ImageRequest{
Name: &aN,
Arch: &arch,
Region: r.Region,
BlockDeviceType: &bdt})
if err != nil {
return err
}
Expand Down Expand Up @@ -296,14 +298,14 @@ func (r *MacRequest) securityGroups(ctx *pulumi.Context,
// Create the mac instance
func (r *MacRequest) instance(ctx *pulumi.Context,
subnet *ec2.Subnet,
ami *ec2.LookupAmiResult,
ami *data.ImageInfo,
keyResources *keypair.KeyPairResources,
securityGroups pulumi.StringArray,
) (*ec2.Instance, error) {
instanceArgs := ec2.InstanceArgs{
HostId: pulumi.String(*r.dedicatedHost.Host.HostId),
SubnetId: subnet.ID(),
Ami: pulumi.String(ami.Id),
Ami: pulumi.String(*ami.Image.ImageId),
InstanceType: pulumi.String(macTypesByArch[r.Architecture]),
KeyName: keyResources.AWSKeyPair.KeyName,
AssociatePublicIpAddress: pulumi.Bool(true),
Expand Down Expand Up @@ -360,19 +362,15 @@ func (r *MacRequest) getBootstrapScript(ctx *pulumi.Context) (
*random.RandomPassword,
*keypair.KeyPairResources,
error) {
password, err := security.CreatePassword(ctx,
resourcesUtil.GetResourceName(r.Prefix, awsMacMachineID, "passwd"))
if err != nil {
return nil, nil, nil, err
}
// Create Keypair for the user
// If replace is enabled we need to re create the pk-user
// name := resourcesUtil.GetResourceName(
// r.Prefix, awsMacMachineID, "pk-user5be3717a")
name := *r.dedicatedHost.RunID
if r.replace {
name = qenvsContext.CreateRunID()
}
password, err := security.CreatePassword(ctx,
name)
if err != nil {
return nil, nil, nil, err
}
ukpr := keypair.KeyPairRequest{
Name: name}
ukp, err := ukpr.Create(ctx)
Expand Down
54 changes: 36 additions & 18 deletions pkg/provider/aws/action/mac/mac.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package mac

import (
_ "embed"
"fmt"
"strings"

qenvsContext "github.com/adrianriobo/qenvs/pkg/manager/context"
"github.com/adrianriobo/qenvs/pkg/provider/aws"
Expand All @@ -28,17 +30,26 @@ import (
//
// ...
func Request(r *MacRequest) error {
// Get list of dedicated host ordered by allocation time
his, err := getMatchingHostsInformation(r.Architecture)
if err != nil {
return err
}
// If no machines we will create one
if len(his) == 0 {
return create(r)
return create(r, nil)
}
// Pick the most suited to be offered to the requester
// and replcae (create fresh env)
hi := pickHost(his)
// If for whatever reason the mac has no been created
// stack does nt exist pick will require create not replace
hi, err := pickHost(r.Prefix, his)
if err != nil {
if hi == nil {
return err
}
return create(r, hi)
}
err = r.replaceMachine(hi)
if err != nil {
return err
Expand Down Expand Up @@ -129,30 +140,37 @@ func Destroy(prefix, hostID string) error {
// It will also create a mac machine based on the arch and version setup
// and will set a lock on it

func create(r *MacRequest) (err error) {
var dh *HostInformation
adh, err := getMatchingAvailableHostsInformation(r.Architecture)
if err != nil {
return err
}
// if no available dh create it
// otherwise pick the first (newest allocated)
if len(adh) == 0 {
func create(r *MacRequest, dh *HostInformation) (err error) {
if dh == nil {
dh, err = r.createDedicatedHost()
if err != nil {
return err
}
} else {
dh = &adh[0]
}
// Setup the topology and install the mac machine
if !r.Airgap {
return r.createMacMachine(*dh)
return r.createMacMachine(dh)
}
return r.createAirgapMacMachine(*dh)
return r.createAirgapMacMachine(dh)
}

// TODO check which on the list is not locked
func pickHost(his []HostInformation) HostInformation {
return his[0]
// We will get a list of hosts from the pool ordered by allocation time
// We will apply several rules on them to pick the right one
// - TODO Remove those with allocation time > 24 h as they may destroyed
// - if none left use them again
// - if more available pick in order the first without lock
func pickHost(prefix string, his []*HostInformation) (*HostInformation, error) {
for _, h := range his {
isLocked, err := isMachineLocked(prefix, h)
if err != nil {
logging.Errorf("error checking if machine %s is locked", *h.Host.HostId)
if strings.Contains(err.Error(), "no stack") {
return h, err
}
}
if !isLocked {
return h, nil
}
}
return nil, fmt.Errorf("all hosts are locked at the moment")
}
23 changes: 13 additions & 10 deletions pkg/provider/aws/action/mac/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import (
)

// Compose information around dedicated host
func getHostInformation(h ec2Types.Host) HostInformation {
func getHostInformation(h ec2Types.Host) *HostInformation {
az := *h.AvailabilityZone
region := az[:len(az)-1]
archValue := awsArchIDbyArch[*getTagValue(h.Tags, tagKeyArch)]
return HostInformation{
return &HostInformation{
Arch: &archValue,
BackedURL: getTagValue(h.Tags, tagKeyBackedURL),
ProjectName: getTagValue(h.Tags, qenvsContext.TagKeyProjectName),
Expand Down Expand Up @@ -45,19 +45,20 @@ func getBackedURL() string {
}

// Get all dedicated hosts matching the tags + arch
func getMatchingHostsInformation(arch string) ([]HostInformation, error) {
// it will return the list ordered by allocation time
func getMatchingHostsInformation(arch string) ([]*HostInformation, error) {
return getMatchingHostsInStateInformation(arch, nil)
}

// Get all dedicated hosts in available state ordered based on the allocation time
// newer allocations go first
func getMatchingAvailableHostsInformation(arch string) ([]HostInformation, error) {
as := ec2Types.AllocationStateAvailable
return getMatchingHostsInStateInformation(arch, &as)
}
// func getMatchingAvailableHostsInformation(arch string) ([]HostInformation, error) {
// as := ec2Types.AllocationStateAvailable
// return getMatchingHostsInStateInformation(arch, &as)
// }

// Get all dedicated hosts by tag and state
func getMatchingHostsInStateInformation(arch string, state *ec2Types.AllocationState) ([]HostInformation, error) {
func getMatchingHostsInStateInformation(arch string, state *ec2Types.AllocationState) ([]*HostInformation, error) {
matchingTags := qenvsContext.GetTags()
matchingTags[tagKeyArch] = arch
hosts, err := data.GetDedicatedHosts(data.DedicatedHostResquest{
Expand All @@ -66,15 +67,15 @@ func getMatchingHostsInStateInformation(arch string, state *ec2Types.AllocationS
if err != nil {
return nil, err
}
var r []HostInformation
var r []*HostInformation
for _, dh := range hosts {
if state == nil || (state != nil && dh.State == *state) {
r = append(r, getHostInformation(dh))
}
}
// Order by allocation time, first newest
if len(r) > 1 {
slices.SortFunc(r, func(a, b HostInformation) int {
slices.SortFunc(r, func(a, b *HostInformation) int {
return b.Host.AllocationTime.Compare(*a.Host.AllocationTime)
})
}
Expand All @@ -96,6 +97,7 @@ func getRegion(r *MacRequest) (*string, error) {
return nil, err
}
if isOffered {
logging.Debugf("%s offers it", os.Getenv("AWS_DEFAULT_REGION"))
region := os.Getenv("AWS_DEFAULT_REGION")
return &region, nil
}
Expand All @@ -105,6 +107,7 @@ func getRegion(r *MacRequest) (*string, error) {
os.Getenv("AWS_DEFAULT_REGION"))
}
// We look for a region offering the type of instance
logging.Debugf("%s is not offered, a new region offering it will be used instead", os.Getenv("AWS_DEFAULT_REGION"))
return data.LokupRegionOfferingInstanceType(
macTypesByArch[r.Architecture])
}
Expand Down
Loading

0 comments on commit 6896c8e

Please sign in to comment.