Skip to content

Commit

Permalink
refactor: set VOR stream images dynamically instead of manually (#33)
Browse files Browse the repository at this point in the history
* refactor: modified filters to 2D array

* refactor: modified the process ami is retrieved

* fix: code quality check

* fix: changed public filter value from true to false

* refactor: sort amis by date created

* fix: modified map to struct to keep sort results

* refactor: modified form to display data from json object instead of map

* fix: code quality

* fix: remove empty strings from ami config

* fix: tags not applied to deployed spot instance

* fix: referenced wrong resource

* refactor: display only ami in available state
  • Loading branch information
luqmanbazran authored Aug 29, 2024
1 parent 85d44ce commit 5ecc109
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,8 @@
<mat-form-field floatLabel="always" appearance="outline">
<mat-label>AMI Choice</mat-label>
<mat-select formControlName="ami">
<mat-option
*ngFor="let ami of amis | keyvalue"
[value]="ami.key"
>({{ ami.key }}) {{ ami.value }}</mat-option
<mat-option *ngFor="let ami of amis" [value]="ami.amiIds"
>({{ ami.amiIds }}) {{ ami.amiNames }}</mat-option
>
</mat-select>
</mat-form-field>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { ApiService } from '../shared/services/api.service';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Lifecycle, TimeUnit } from '../shared/enum/dropdown.enum';
import { Lifecycle, TimeUnit, AmiAttr } from '../shared/enum/dropdown.enum';
import { DeploymentApiRequest } from '../shared/model/deployment-request';
import { Router } from '@angular/router';
import { MatSnackBar } from '@angular/material/snack-bar';
Expand All @@ -18,7 +18,7 @@ export class CreateDeploymentComponent implements OnInit {

deploymentForm!: FormGroup;
serverSizes: string[] = [];
amis: Map<string, string> = new Map<string, string>();
amis: AmiAttr[] = [];
region: string = '';
lifecycles: Lifecycle[] = [Lifecycle.ON_DEMAND, Lifecycle.SPOT];
ttlUnits: TimeUnit[] = [TimeUnit.HOURS, TimeUnit.DAYS, TimeUnit.MONTHS];
Expand Down Expand Up @@ -67,9 +67,8 @@ export class CreateDeploymentComponent implements OnInit {
this.serverSizes = data.serverSizes;
this.amis = data.amis;
this.region = data.regions;

this.deploymentForm.get('serverSize')?.patchValue('t3.medium');
this.deploymentForm.get('ami')?.patchValue(this.amis);
this.deploymentForm.get('ami')?.patchValue(this.amis[0].amiIds);
this.deploymentForm.get('region')?.patchValue(this.region);
});
}
Expand Down
5 changes: 5 additions & 0 deletions client/src/app/shared/enum/dropdown.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ export const enum TimeUnit {
DAYS = 'day',
MONTHS = 'month',
}

export interface AmiAttr {
amiIds: string;
amiNames: string;
}
37 changes: 37 additions & 0 deletions ecr-scripts/instance.tf
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,43 @@ resource "aws_spot_instance_request" "my_deployed_spot_instances" {
TimeToExpire = each.value.timeToExpire
DeployedBy = "turbo-deploy"
}
wait_for_fulfillment = true
}

// the tags specified in the spot request only applies to the request not the instances
resource "aws_ec2_tag" "name" {
for_each = aws_spot_instance_request.my_deployed_spot_instances
resource_id = aws_spot_instance_request.my_deployed_spot_instances[each.key].spot_instance_id
key = "Name"
value = each.value.tags_all.Name
}

resource "aws_ec2_tag" "hostname" {
for_each = aws_spot_instance_request.my_deployed_spot_instances
resource_id = aws_spot_instance_request.my_deployed_spot_instances[each.key].spot_instance_id
key = "Hostname"
value = each.value.tags_all.Hostname
}

resource "aws_ec2_tag" "deploymentid" {
for_each = aws_spot_instance_request.my_deployed_spot_instances
resource_id = aws_spot_instance_request.my_deployed_spot_instances[each.key].spot_instance_id
key = "DeploymentID"
value = each.value.tags_all.DeploymentID
}

resource "aws_ec2_tag" "timetoexpire" {
for_each = aws_spot_instance_request.my_deployed_spot_instances
resource_id = aws_spot_instance_request.my_deployed_spot_instances[each.key].spot_instance_id
key = "TimeToExpire"
value = each.value.tags_all.TimeToExpire
}

resource "aws_ec2_tag" "deployedby" {
for_each = aws_spot_instance_request.my_deployed_spot_instances
resource_id = aws_spot_instance_request.my_deployed_spot_instances[each.key].spot_instance_id
key = "DeployedBy"
value = each.value.tags_all.DeployedBy
}

resource "aws_route53_record" "spot_record" {
Expand Down
68 changes: 60 additions & 8 deletions server/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func init() {

// setup allowed origins
config := cors.DefaultConfig()
config.AllowOrigins = []string{fmt.Sprintf("http://%s:%s", fullName, httpPortEnv), fmt.Sprintf("https://%s:%s", fullName, httpsPortEnv)}
config.AllowOrigins = []string{fmt.Sprintf("http://%s:%s", fullName, httpPortEnv), fmt.Sprintf("https://%s:%s", fullName, httpsPortEnv), fmt.Sprintf("https://%s", fullName), fmt.Sprintf("https://%s", fullName)}
r.Use(cors.New(config))

SetupRoutes(r)
Expand Down Expand Up @@ -261,27 +261,79 @@ func GetAWSData(c *gin.Context) {

tempConfig := models.TempConfig{}

// add region env
tempConfig.Region = regionEnv

// get list of AMIs from the env
err := json.Unmarshal([]byte(configEnv), &tempConfig)
if err != nil {
log.Printf("Error parsing environment variable: %v", err)
abortWithLog(c, http.StatusInternalServerError, err)
return
}

// add region env
tempConfig.Region = regionEnv
// Remove empty strings from the Ami config
i := 0
for _, ami := range tempConfig.Ami {
if ami != "" {
tempConfig.Ami[i] = ami
i++
}
}
tempConfig.Ami = tempConfig.Ami[:i]

// get list of snapshot AMIs(AMI created by user)
snapshotFilter := []types.Filter{
{
Name: aws.String("is-public"),
Values: []string{"false"},
},
{
Name: aws.String("tag:DeployedBy"),
Values: []string{"turbo-deploy"},
},
{
Name: aws.String("state"),
Values: []string{"available"},
},
}

// get latest VOR AMI
vorFilter := []types.Filter{
{
Name: aws.String("is-public"),
Values: []string{"false"},
},
{
Name: aws.String("name"),
Values: []string{"VOR Stream*"},
},
{
Name: aws.String("state"),
Values: []string{"available"},
},
}

// get list of snapshot AMIs, and add to the AMI available
tempConfig.Ami, err = instance.GetAvailableAmis(tempConfig.Ami)
filterGroup := [][]types.Filter{
snapshotFilter,
vorFilter,
}

// add the amis retrieved based on filters given
tempConfig.Ami, err = instance.GetAvailableAmis(tempConfig.Ami, filterGroup)
if err != nil {
log.Printf("Failed to get list of AMIs: %v", err)
return
}

// get the names of the ami
m := make(map[string]string)
for _, v := range tempConfig.Ami {
m[v] = ""
m := []models.AmiAttr{}
for _, id := range tempConfig.Ami {
ami := models.AmiAttr{
AmiID: id,
AmiName: "",
}
m = append(m, ami)
}

m = instance.GetAMIName(m)
Expand Down
56 changes: 26 additions & 30 deletions server/instance/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log"
"sort"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
Expand Down Expand Up @@ -230,44 +231,40 @@ func CaptureInstanceImage(instanceID string) (string, error) {
return aws.ToString(result.ImageId), nil
}

func GetAvailableAmis(amilist []string) ([]string, error) {
// check if an image for that instance already exists
filter := []types.Filter{
{
Name: aws.String("is-public"),
Values: []string{"false"},
},
{
Name: aws.String("tag:DeployedBy"),
Values: []string{"turbo-deploy"},
},
}

imageResult, err := getImage(filter)
if err != nil {
log.Printf("failed to retrieve images: %v", err)
}
func GetAvailableAmis(amilist []string, filterGroup [][]types.Filter) ([]string, error) {
for _, filter := range filterGroup {
imageResult, err := getImage(filter)
if err != nil {
log.Printf("failed to retrieve images: %v", err)
}

// if it exists grab it
if len(imageResult.Images) == 0 {
log.Printf("No images returned for extra listing")
} else {
for i := range imageResult.Images {
image := imageResult.Images[i]
log.Printf("Image %s found, appending to the list...", *image.ImageId)
amilist = append(amilist, *image.ImageId)
// if it exists grab it
if len(imageResult.Images) == 0 {
log.Printf("No images returned for extra listing")
} else {
// sort the images found by creation date
sort.Slice(imageResult.Images, func(i, j int) bool {
timeI, _ := time.Parse(time.RFC3339, *imageResult.Images[i].CreationDate)
timeJ, _ := time.Parse(time.RFC3339, *imageResult.Images[j].CreationDate)
return timeI.After(timeJ) // For descending order (newest first)
})
for i := range imageResult.Images {
image := imageResult.Images[i]
log.Printf("Image %s found, appending to the list...", *image.ImageId)
amilist = append(amilist, *image.ImageId)
}
}
}

return amilist, nil
}

func GetAMIName(ami map[string]string) map[string]string {
for k := range ami {
func GetAMIName(ami []models.AmiAttr) []models.AmiAttr {
for i := range ami {
filter := []types.Filter{
{
Name: aws.String("image-id"),
Values: []string{k},
Values: []string{ami[i].AmiID},
},
}

Expand All @@ -276,8 +273,7 @@ func GetAMIName(ami map[string]string) map[string]string {
log.Printf("failed to retrieve images: %v", err)
}

ami[k] = *imageResult.Images[0].Name

ami[i].AmiName = *imageResult.Images[0].Name
}

return ami
Expand Down
11 changes: 8 additions & 3 deletions server/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ type Payload struct {
}

type Config struct {
Ami map[string]string `json:"amis"`
Region string `json:"regions"`
ServerSizes []string `json:"serverSizes"`
Ami []AmiAttr `json:"amis"`
Region string `json:"regions"`
ServerSizes []string `json:"serverSizes"`
}

type TempConfig struct {
Expand All @@ -44,6 +44,11 @@ type TempConfig struct {
Ami []string `json:"amis"`
}

type AmiAttr struct {
AmiID string `json:"amiIds"`
AmiName string `json:"amiNames"`
}

type DeploymentResponse struct {
DeploymentID string `json:"deploymentId"` // Represents dynamoDB id that created this instance
InstanceID string `json:"ec2InstanceId"` // Represents ec2 instance id
Expand Down

0 comments on commit 5ecc109

Please sign in to comment.