Files
vm/service/aws.go
zooqueenandHanzo Dev a121f8d0d0 refactor: rebrand from casvisor to hanzoai/visor, remove hardcoded credentials
- Rename Go module from github.com/casvisor/casvisor to github.com/hanzoai/visor
- Update all copyright headers from casbin to Hanzo Industries Inc
- Remove all hardcoded passwords and credentials from config files
- All credentials now sourced from environment variables / Hanzo KMS
- Switch default database from MySQL to PostgreSQL (matching production)
- Rename binary output from server/casvisor to visor
- Update Dockerfile, build.sh, docker-compose.yml, docker-entrypoint.sh
- Replace casdoor* config key references with iam* equivalents
- Update CI to use PostgreSQL service and mark CI-only test password
- Add visor and casvisor binaries to .gitignore
- Add envFrom secretRef for visor-env in K8s deployment
- Add ExternalSecret template for Hanzo KMS credential provisioning

Co-authored-by: Hanzo Dev <[email protected]>
2026-02-21 00:04:31 -08:00

191 lines
5.1 KiB
Go

// Copyright 2023 Hanzo Industries Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
)
type MachineAwsClient struct {
Client *ec2.Client
region string
}
func newMachineAwsClient(accessKeyId string, accessKeySecret string, region string) (MachineAwsClient, error) {
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRegion(region),
config.WithCredentialsProvider(aws.CredentialsProviderFunc(func(ctx context.Context) (aws.Credentials, error) {
return aws.Credentials{
AccessKeyID: accessKeyId,
SecretAccessKey: accessKeySecret,
}, nil
})),
)
if err != nil {
return MachineAwsClient{}, err
}
client := ec2.NewFromConfig(cfg)
return MachineAwsClient{Client: client, region: region}, nil
}
func getMachineFromAwsInstance(instance ec2Types.Instance) *Machine {
machine := &Machine{
Name: *instance.InstanceId,
Id: *instance.InstanceId,
Region: *instance.Placement.AvailabilityZone,
DisplayName: *instance.InstanceId,
}
if instance.InstanceType != "" {
machine.Size = string(instance.InstanceType)
}
if len(instance.Tags) > 0 {
for _, tag := range instance.Tags {
machine.Tag += fmt.Sprintf("%s=%s,", *tag.Key, *tag.Value)
}
}
machine.State = string(instance.State.Name)
return machine
}
func (client MachineAwsClient) GetMachines() ([]*Machine, error) {
input := &ec2.DescribeInstancesInput{}
output, err := client.Client.DescribeInstances(context.TODO(), input)
if err != nil {
return nil, err
}
machines := []*Machine{}
for _, reservation := range output.Reservations {
for _, instance := range reservation.Instances {
machine := getMachineFromAwsInstance(instance)
machines = append(machines, machine)
}
}
return machines, nil
}
func (client MachineAwsClient) GetMachine(name string) (*Machine, error) {
input := &ec2.DescribeInstancesInput{
InstanceIds: []string{name},
}
output, err := client.Client.DescribeInstances(context.TODO(), input)
if err != nil {
return nil, err
}
if len(output.Reservations) == 0 || len(output.Reservations[0].Instances) == 0 {
return nil, fmt.Errorf("Instance not found: %s", name)
}
instance := output.Reservations[0].Instances[0]
return getMachineFromAwsInstance(instance), nil
}
func (client MachineAwsClient) UpdateMachineState(name string, state string) (bool, string, error) {
switch state {
case "Running":
input := &ec2.StartInstancesInput{
InstanceIds: []string{name},
}
_, err := client.Client.StartInstances(context.TODO(), input)
if err != nil {
return false, "", err
}
case "Stopped":
input := &ec2.StopInstancesInput{
InstanceIds: []string{name},
}
_, err := client.Client.StopInstances(context.TODO(), input)
if err != nil {
return false, "", err
}
default:
return false, fmt.Sprintf("Unsupported state: %s", state), nil
}
return true, fmt.Sprintf("Instance: [%s]'s state has been successfully updated to: [%s]", name, state), nil
}
func (client MachineAwsClient) CreateMachine(spec *CreateMachineSpec) (*Machine, error) {
if spec.ImageID == "" {
return nil, fmt.Errorf("imageId is required for AWS instance creation")
}
instanceType := ec2Types.InstanceType(spec.InstanceType)
if spec.InstanceType == "" {
instanceType = ec2Types.InstanceTypeT3Medium
}
// Build tags
var tags []ec2Types.Tag
tags = append(tags, ec2Types.Tag{
Key: aws.String("Name"),
Value: aws.String(spec.DisplayName),
})
if spec.OS != "" {
tags = append(tags, ec2Types.Tag{
Key: aws.String("OS"),
Value: aws.String(spec.OS),
})
}
tags = append(tags, ec2Types.Tag{
Key: aws.String("ManagedBy"),
Value: aws.String("hanzo-visor"),
})
for k, v := range spec.Tags {
tags = append(tags, ec2Types.Tag{
Key: aws.String(k),
Value: aws.String(v),
})
}
input := &ec2.RunInstancesInput{
ImageId: aws.String(spec.ImageID),
InstanceType: instanceType,
MinCount: aws.Int32(1),
MaxCount: aws.Int32(1),
TagSpecifications: []ec2Types.TagSpecification{
{
ResourceType: ec2Types.ResourceTypeInstance,
Tags: tags,
},
},
}
output, err := client.Client.RunInstances(context.TODO(), input)
if err != nil {
return nil, fmt.Errorf("failed to launch EC2 instance: %w", err)
}
if len(output.Instances) == 0 {
return nil, fmt.Errorf("RunInstances returned no instances")
}
return getMachineFromAwsInstance(output.Instances[0]), nil
}