3 changed files with 307 additions and 9 deletions
@ -0,0 +1,202 @@ |
|||||||
|
package simulation |
||||||
|
|
||||||
|
import "encoding/json" |
||||||
|
|
||||||
|
type Node struct { |
||||||
|
ID string `json:"id"` |
||||||
|
Type string `json:"type"` |
||||||
|
Position Position `josn:"position"` |
||||||
|
Props map[string]interface{} `json:"props"` |
||||||
|
} |
||||||
|
|
||||||
|
type Position struct { |
||||||
|
X int `json:"x"` |
||||||
|
Y int `json:"y"` |
||||||
|
} |
||||||
|
|
||||||
|
type Connection struct { |
||||||
|
Source string `json:"source"` |
||||||
|
Target string `json:"target"` |
||||||
|
Label string `json:"label,omitempty"` |
||||||
|
Direction string `json:"direction,omitempty"` |
||||||
|
Protocol string `json:"protocol,omitempty"` |
||||||
|
TLS bool `json:"tls,omitemity"` |
||||||
|
Capacity int `json:"capacity,omitempty"` |
||||||
|
} |
||||||
|
|
||||||
|
type Design struct { |
||||||
|
Nodes []Node `json:"nodes"` |
||||||
|
Connections []Connection |
||||||
|
} |
||||||
|
|
||||||
|
type Cache struct { |
||||||
|
Label string `json:"label"` |
||||||
|
CacheTTL int `json:"cacheTTL"` |
||||||
|
MaxEntries int `json:"maxEntries"` |
||||||
|
EvictionPolicy string `json:"evictionPolicy"` |
||||||
|
} |
||||||
|
|
||||||
|
type CDN struct { |
||||||
|
Label string `json:"label"` |
||||||
|
TTL int `json:"ttl"` |
||||||
|
GeoReplication string `json:"geoReplication"` |
||||||
|
CachingStrategy string `json:"cachingStrategy"` |
||||||
|
Compression string `json:"compression"` |
||||||
|
HTTP2 string `json:"http2"` |
||||||
|
} |
||||||
|
|
||||||
|
type Database struct { |
||||||
|
Label string `json:"label"` |
||||||
|
Replication int `json:"replication"` |
||||||
|
} |
||||||
|
|
||||||
|
type DataPipeline struct { |
||||||
|
Label string `json:"label"` |
||||||
|
BatchSize int `json:"batchSize"` |
||||||
|
Transformation string `json:"transformation"` |
||||||
|
} |
||||||
|
|
||||||
|
type LoadBalancer struct { |
||||||
|
Label string `json:"label"` |
||||||
|
Algorithm string `json:"algorithm"` |
||||||
|
} |
||||||
|
|
||||||
|
type MessageQueue struct { |
||||||
|
Label string `json:"label"` |
||||||
|
QueueCapacity int `json:"queueCapacity"` |
||||||
|
RetentionSeconds int `json:"retentionSeconds"` |
||||||
|
} |
||||||
|
|
||||||
|
type Microservice struct { |
||||||
|
Label string `json:"label"` |
||||||
|
InstanceCount int `json:"instanceCount"` |
||||||
|
CPU int `json:"cpu"` |
||||||
|
RAMGb int `json:"ramGb"` |
||||||
|
RPSCapacity int `json:"rpsCapacity"` |
||||||
|
MonthlyUSD int `json:"monthlyUsd"` |
||||||
|
ScalingStrategy string `json:"scalingStrategy"` |
||||||
|
APIVersion string `json:"apiVersion"` |
||||||
|
} |
||||||
|
|
||||||
|
type Monitoring struct { |
||||||
|
Label string `json:"label"` |
||||||
|
Tool string `json:"tool"` |
||||||
|
AlertMetric string `json:"alertMetric"` // e.g., "cpu", "latency"
|
||||||
|
ThresholdValue int `json:"thresholdValue"` // e.g., 80
|
||||||
|
ThresholdUnit string `json:"thresholdUnit"` // e.g., "percent", "ms"
|
||||||
|
} |
||||||
|
|
||||||
|
type ThirdPartyService struct { |
||||||
|
Label string `json:"label"` |
||||||
|
Provider string `json:"provider"` |
||||||
|
Latency int `json:"latency"` |
||||||
|
} |
||||||
|
|
||||||
|
type WebServer struct { |
||||||
|
CPU int `json:"cpu"` |
||||||
|
RamGb int `json:"ramGb"` |
||||||
|
RPSCapacity int `json:"rpsCapacity"` |
||||||
|
MonthlyCostUsd int `json:"monthlyCostUsd"` |
||||||
|
} |
||||||
|
|
||||||
|
func (n *Node) UnmarshalJSON(data []byte) error { |
||||||
|
type Alias Node // avoid infinite recursion
|
||||||
|
aux := &struct { |
||||||
|
Props json.RawMessage `json:"props"` |
||||||
|
*Alias |
||||||
|
}{ |
||||||
|
Alias: (*Alias)(n), |
||||||
|
} |
||||||
|
|
||||||
|
if err := json.Unmarshal(data, aux); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
switch n.Type { |
||||||
|
case "cache": |
||||||
|
var p Cache |
||||||
|
if err := json.Unmarshal(aux.Props, &p); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = structToMap(p) |
||||||
|
|
||||||
|
case "cdn": |
||||||
|
var p CDN |
||||||
|
if err := json.Unmarshal(aux.Props, &p); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = structToMap(p) |
||||||
|
|
||||||
|
case "database": |
||||||
|
var p Database |
||||||
|
if err := json.Unmarshal(aux.Props, &p); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = structToMap(p) |
||||||
|
|
||||||
|
case "data pipeline": |
||||||
|
var p DataPipeline |
||||||
|
if err := json.Unmarshal(aux.Props, &p); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = structToMap(p) |
||||||
|
|
||||||
|
case "loadBalancer": |
||||||
|
var p LoadBalancer |
||||||
|
if err := json.Unmarshal(aux.Props, &p); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = structToMap(p) |
||||||
|
|
||||||
|
case "messageQueue": |
||||||
|
var p MessageQueue |
||||||
|
if err := json.Unmarshal(aux.Props, &p); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = structToMap(p) |
||||||
|
|
||||||
|
case "microservice": |
||||||
|
var p Microservice |
||||||
|
if err := json.Unmarshal(aux.Props, &p); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = structToMap(p) |
||||||
|
|
||||||
|
case "monitoring/alerting": |
||||||
|
var p Monitoring |
||||||
|
if err := json.Unmarshal(aux.Props, &p); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = structToMap(p) |
||||||
|
|
||||||
|
case "third party service": |
||||||
|
var p ThirdPartyService |
||||||
|
if err := json.Unmarshal(aux.Props, &p); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = structToMap(p) |
||||||
|
|
||||||
|
case "webserver": |
||||||
|
var p WebServer |
||||||
|
if err := json.Unmarshal(aux.Props, &p); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = structToMap(p) |
||||||
|
|
||||||
|
default: |
||||||
|
var generic map[string]interface{} |
||||||
|
if err := json.Unmarshal(aux.Props, &generic); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
n.Props = generic |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func structToMap(v interface{}) map[string]interface{} { |
||||||
|
data, _ := json.Marshal(v) |
||||||
|
var result map[string]interface{} |
||||||
|
json.Unmarshal(data, &result) |
||||||
|
return result |
||||||
|
} |
||||||
@ -0,0 +1,105 @@ |
|||||||
|
package level |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"os" |
||||||
|
) |
||||||
|
|
||||||
|
type Level struct { |
||||||
|
ID string `json:"id"` |
||||||
|
Name string `json:"name"` |
||||||
|
Description string `json:"description"` |
||||||
|
Difficulty Difficulty `json:"difficulty"` |
||||||
|
|
||||||
|
TargetRPS int `json:"targetRps"` |
||||||
|
DurationSec int `json:"durationSec"` |
||||||
|
MaxMonthlyUSD int `json:"maxMonthlyUsd"` |
||||||
|
MaxP95LatencyMs int `json:"maxP95LatencyMs"` |
||||||
|
RequiredAvailabilityPct float64 `json:"requiredAvailabilityPct"` |
||||||
|
|
||||||
|
MustInclude []string `json:"mustInclude,omitempty"` |
||||||
|
MustNotInclude []string `json:"mustNotInclude,omitempty"` |
||||||
|
EncouragedComponents []string `json:"encouragedComponents,omitempty"` |
||||||
|
DiscouragedComponents []string `json:"discouragedComponents,omitempty"` |
||||||
|
MinReplicas map[string]int `json:"minReplicas,omitempty"` |
||||||
|
MaxLatencyPerNodeType map[string]int `json:"maxLatencyPerNodeType,omitempty"` |
||||||
|
CustomValidators []string `json:"customValidators,omitempty"` |
||||||
|
|
||||||
|
FailureEvents []FailureEvent `json:"failureEvents,omitempty"` |
||||||
|
ScoringWeights map[string]float64 `json:"scoringWeights,omitempty"` |
||||||
|
|
||||||
|
Hints []string `json:"hints,omitempty"` |
||||||
|
} |
||||||
|
|
||||||
|
type Difficulty string |
||||||
|
|
||||||
|
const ( |
||||||
|
DifficultyEasy Difficulty = "easy" |
||||||
|
DifficultyMedium Difficulty = "medium" |
||||||
|
DifficultyHard Difficulty = "hard" |
||||||
|
) |
||||||
|
|
||||||
|
var Registry map[string]map[string]Level |
||||||
|
|
||||||
|
type FailureEvent struct { |
||||||
|
Type string `json:"type"` |
||||||
|
TimeSec int `json:"timeSec"` |
||||||
|
TargetID string `json:"targetId,omitempty"` |
||||||
|
} |
||||||
|
|
||||||
|
func LoadLevels(path string) ([]Level, error) { |
||||||
|
file, err := os.Open(path) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("Error opening levels.json: %w", err) |
||||||
|
} |
||||||
|
defer file.Close() |
||||||
|
|
||||||
|
var levels []Level |
||||||
|
err = json.NewDecoder(file).Decode(&levels) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("Error decoding levels.json: %w", err) |
||||||
|
} |
||||||
|
|
||||||
|
return levels, nil |
||||||
|
} |
||||||
|
|
||||||
|
func InitRegistry(levels []Level) { |
||||||
|
Registry = make(map[string]map[string]Level) |
||||||
|
for _, lvl := range levels { |
||||||
|
// check if level already exists here
|
||||||
|
if _, ok := Registry[lvl.Name]; !ok { |
||||||
|
Registry[lvl.Name] = make(map[string]Level) |
||||||
|
} |
||||||
|
// populate it
|
||||||
|
Registry[lvl.Name][string(lvl.Difficulty)] = lvl |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func GetLevel(name string, difficulty Difficulty) (*Level, error) { |
||||||
|
diffMap, ok := Registry[name] |
||||||
|
if !ok { |
||||||
|
return nil, fmt.Errorf("level name %s not found", name) |
||||||
|
} |
||||||
|
|
||||||
|
lvl, ok := diffMap[string(difficulty)] |
||||||
|
if !ok { |
||||||
|
return nil, fmt.Errorf("difficulty %s not available for level '%s'", difficulty, name) |
||||||
|
} |
||||||
|
return &lvl, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (d *Difficulty) UnmarshalJSON(b []byte) error { |
||||||
|
var s string |
||||||
|
if err := json.Unmarshal(b, &s); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
switch s { |
||||||
|
case string(DifficultyEasy), string(DifficultyMedium), string(DifficultyHard): |
||||||
|
*d = Difficulty(s) |
||||||
|
return nil |
||||||
|
default: |
||||||
|
return fmt.Errorf("invalid difficulty: %q", s) |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue