mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 09:01:31 +00:00
Updated config and admin init, awsgw tweaks and templates
This commit is contained in:
@@ -0,0 +1,620 @@
|
||||
/*
|
||||
Copyright © 2025 Mulga Defense Corporation
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
_ "embed"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/mulgadc/viperblock/viperblock"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
//go:embed templates/hive.toml
|
||||
var hiveTomlTemplate string
|
||||
|
||||
//go:embed templates/awsgw.toml
|
||||
var awsgwTomlTemplate string
|
||||
|
||||
//go:embed templates/predastore.toml
|
||||
var predastoreTomlTemplate string
|
||||
|
||||
//go:embed templates/nats.conf
|
||||
var natsConfTemplate string
|
||||
|
||||
var supportedArchs = map[string]bool{
|
||||
"x86_64": true,
|
||||
"aarch64": true,
|
||||
"arm64": true,
|
||||
}
|
||||
|
||||
// TODO: Confirm suppported platform types
|
||||
var supportedPlatforms = map[string]bool{
|
||||
"Linux/UNIX": true,
|
||||
"Windows": true,
|
||||
}
|
||||
|
||||
var adminCmd = &cobra.Command{
|
||||
Use: "admin",
|
||||
Short: "Administrative commands for Hive platform management",
|
||||
Long: `Administrative commands for initializing and managing the Hive platform.`,
|
||||
}
|
||||
|
||||
var adminInitCmd = &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Initialize Hive platform configuration",
|
||||
Long: `Initialize Hive platform by creating configuration files, generating SSL certificates,
|
||||
and setting up AWS credentials. This creates the necessary directory structure and
|
||||
configuration files in ~/hive/config.`,
|
||||
Run: runAdminInit,
|
||||
}
|
||||
|
||||
var imagesCmd = &cobra.Command{
|
||||
Use: "images",
|
||||
Short: "Manage OS images",
|
||||
Long: `Manage OS images for local storage and AMI creation.`,
|
||||
}
|
||||
|
||||
var imagesImportCmd = &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Specify local file to import",
|
||||
Long: `Create a new image from a local file`,
|
||||
Run: runimagesImportCmd,
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(adminCmd)
|
||||
adminCmd.AddCommand(adminInitCmd)
|
||||
|
||||
adminCmd.AddCommand(imagesCmd)
|
||||
imagesCmd.AddCommand(imagesImportCmd)
|
||||
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
configDir := fmt.Sprintf("%s/hive/config", homeDir)
|
||||
hiveDir := fmt.Sprintf("%s/hive/", homeDir)
|
||||
|
||||
rootCmd.PersistentFlags().String("config-dir", configDir, "Configuration directory")
|
||||
rootCmd.PersistentFlags().String("hive-dir", hiveDir, "Hive base directory")
|
||||
|
||||
// Flags for admin init
|
||||
adminInitCmd.Flags().Bool("force", false, "Force re-initialization (overwrites existing config)")
|
||||
adminInitCmd.Flags().String("region", "ap-southeast-2", "Mulga region to create")
|
||||
|
||||
imagesImportCmd.Flags().String("file", "", "Import file from specified path (raw, qcow2, compressed)")
|
||||
imagesImportCmd.Flags().String("distro", "", "Specified distro name (e.g debian)")
|
||||
imagesImportCmd.Flags().String("version", "", "Specified distro version (e.g 12)")
|
||||
imagesImportCmd.Flags().String("arch", "", "Specified distro arch (e.g aarch64, arm64, x86_64)")
|
||||
imagesImportCmd.Flags().String("platform", "Linux/UNIX", "Specified platform (e.g Linux/UNIX, Windows)")
|
||||
|
||||
}
|
||||
|
||||
func runimagesImportCmd(cmd *cobra.Command, args []string) {
|
||||
|
||||
imageFile, _ := cmd.Flags().GetString("file")
|
||||
|
||||
if imageFile == "" {
|
||||
fmt.Fprintf(os.Stderr, "File required to import image")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
imageStat, err := os.Stat(imageFile)
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "File could not be found", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
//configDir, _ := cmd.Flags().GetString("config-dir")
|
||||
baseDir, _ := cmd.Flags().GetString("hive-dir")
|
||||
|
||||
// Check the base dir has our images path, and correctlty init
|
||||
imageDir := fmt.Sprintf("%s/images", baseDir)
|
||||
|
||||
if !fileExists(imageDir) {
|
||||
fmt.Fprintf(os.Stderr, "Image directory does not exist. Base path specified correctly?", imageDir)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
distro, _ := cmd.Flags().GetString("distro")
|
||||
version, _ := cmd.Flags().GetString("version")
|
||||
arch, _ := cmd.Flags().GetString("arch")
|
||||
platform, _ := cmd.Flags().GetString("platform")
|
||||
|
||||
if distro == "" {
|
||||
fmt.Fprintf(os.Stderr, "Specify distro name")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Check version specified
|
||||
if version == "" {
|
||||
fmt.Fprintf(os.Stderr, "Specify image version")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !supportedArchs[arch] {
|
||||
fmt.Fprintf(os.Stderr, "Unsupported architecture")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !supportedPlatforms[platform] {
|
||||
fmt.Fprintf(os.Stderr, "Unsupported platform")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Create the specified image directory
|
||||
imagePath := fmt.Sprintf("%s/%s/%s/%s", imageDir, distro, version, arch)
|
||||
|
||||
// Create config directory
|
||||
if err := os.MkdirAll(imagePath, 0700); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating config directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Created config directory: %s\n", imagePath)
|
||||
|
||||
// Create the specified manifest file to describe the image/AMI
|
||||
manifest := viperblock.VolumeConfig{}
|
||||
|
||||
// Calculate the size
|
||||
manifest.AMIMetadata.Name = fmt.Sprintf("%s-%s-%s-ami", distro, version, arch)
|
||||
|
||||
manifest.AMIMetadata.Description = fmt.Sprintf("%s cloud image prepared for Hive", manifest.AMIMetadata.Name)
|
||||
manifest.AMIMetadata.Architecture = arch
|
||||
manifest.AMIMetadata.PlatformDetails = platform
|
||||
manifest.AMIMetadata.CreationDate = time.Now()
|
||||
manifest.AMIMetadata.RootDeviceType = "ebs"
|
||||
manifest.AMIMetadata.Virtualization = "hvm"
|
||||
manifest.AMIMetadata.ImageOwnerAlias = "system"
|
||||
manifest.AMIMetadata.VolumeSizeGiB = uint64(imageStat.Size() / 1024 / 1024 / 1024)
|
||||
|
||||
// Volume Data
|
||||
manifest.VolumeMetadata.VolumeName = manifest.AMIMetadata.Name
|
||||
manifest.VolumeMetadata.TenantID = "system"
|
||||
manifest.VolumeMetadata.SizeGiB = manifest.AMIMetadata.VolumeSizeGiB
|
||||
manifest.VolumeMetadata.State = "available"
|
||||
manifest.VolumeMetadata.AvailabilityZone = "" // TODO: Confirm
|
||||
manifest.VolumeMetadata.CreatedAt = time.Now()
|
||||
manifest.VolumeMetadata.VolumeType = "gp3"
|
||||
manifest.VolumeMetadata.IOPS = 1000
|
||||
|
||||
// Write the manifest to disk
|
||||
// Save as JSON
|
||||
jsonData, err := json.Marshal(manifest)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not marshal manifest: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
manifestFilename := fmt.Sprintf("%s/%s.json", imagePath, manifest.AMIMetadata.Name)
|
||||
// Write to file
|
||||
err = os.WriteFile(manifestFilename, jsonData, 0644)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not write manifest: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func runAdminInit(cmd *cobra.Command, args []string) {
|
||||
force, _ := cmd.Flags().GetBool("force")
|
||||
configDir, _ := cmd.Flags().GetString("config-dir")
|
||||
region, _ := cmd.Flags().GetString("region")
|
||||
|
||||
// Default config directory
|
||||
if configDir == "" {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error getting home directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
configDir = filepath.Join(homeDir, "hive", "config")
|
||||
}
|
||||
|
||||
fmt.Println("🚀 Initializing Hive platform...")
|
||||
fmt.Printf("Configuration directory: %s\n\n", configDir)
|
||||
|
||||
// Check if already initialized
|
||||
hiveTomlPath := filepath.Join(configDir, "hive.toml")
|
||||
if !force && fileExists(hiveTomlPath) {
|
||||
fmt.Println("⚠️ Hive already initialized!")
|
||||
fmt.Printf("Config file exists: %s\n", hiveTomlPath)
|
||||
fmt.Println("\nTo re-initialize, run with --force flag:")
|
||||
fmt.Println(" hive admin init --force")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Create config directory
|
||||
if err := os.MkdirAll(configDir, 0700); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating config directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("✅ Created config directory: %s\n", configDir)
|
||||
|
||||
// Generate AWS credentials
|
||||
accessKey := generateAWSAccessKey()
|
||||
secretKey := generateAWSSecretKey()
|
||||
accountID := generateAccountID()
|
||||
|
||||
fmt.Println("\n🔑 Generated AWS credentials:")
|
||||
fmt.Printf(" Access Key: %s\n", accessKey)
|
||||
fmt.Printf(" Secret Key: %s\n", secretKey)
|
||||
fmt.Printf(" Account ID: %s\n", accountID)
|
||||
|
||||
// Generate SSL certificates
|
||||
certPath := filepath.Join(configDir, "server.pem")
|
||||
keyPath := filepath.Join(configDir, "server.key")
|
||||
|
||||
if force || !fileExists(certPath) || !fileExists(keyPath) {
|
||||
fmt.Println("\n🔐 Generating SSL certificates...")
|
||||
if err := generateSelfSignedCert(certPath, keyPath); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error generating SSL certificates: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("✅ SSL certificates generated:\n")
|
||||
fmt.Printf(" Certificate: %s\n", certPath)
|
||||
fmt.Printf(" Key: %s\n", keyPath)
|
||||
} else {
|
||||
fmt.Println("\n✅ SSL certificates already exist")
|
||||
}
|
||||
|
||||
// Generate NATS token
|
||||
natsToken := generateNATSToken()
|
||||
fmt.Println("\n🔒 Generated NATS authentication token")
|
||||
|
||||
// Get home directory for data path
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
hiveRoot := filepath.Join(homeDir, "hive")
|
||||
|
||||
// Create config files from embedded templates
|
||||
fmt.Println("\n📝 Creating configuration files...")
|
||||
|
||||
// Create subdirectories
|
||||
awsgwDir := filepath.Join(configDir, "awsgw")
|
||||
predastoreDir := filepath.Join(configDir, "predastore")
|
||||
natsDir := filepath.Join(configDir, "nats")
|
||||
|
||||
for _, dir := range []string{awsgwDir, predastoreDir, natsDir} {
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating directory %s: %v\n", dir, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate all config files
|
||||
configs := []struct {
|
||||
path string
|
||||
template string
|
||||
name string
|
||||
}{
|
||||
{hiveTomlPath, hiveTomlTemplate, "hive.toml"},
|
||||
{filepath.Join(awsgwDir, "awsgw.toml"), awsgwTomlTemplate, "awsgw/awsgw.toml"},
|
||||
{filepath.Join(predastoreDir, "predastore.toml"), predastoreTomlTemplate, "predastore/predastore.toml"},
|
||||
{filepath.Join(natsDir, "nats.conf"), natsConfTemplate, "nats/nats.conf"},
|
||||
}
|
||||
|
||||
for _, cfg := range configs {
|
||||
if err := generateConfigFile(cfg.path, cfg.template, accessKey, secretKey, accountID, region, natsToken, hiveRoot); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating %s: %v\n", cfg.name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("✅ Created: %s\n", cfg.name)
|
||||
}
|
||||
|
||||
// Update ~/.aws/credentials and ~/.aws/config
|
||||
fmt.Println("\n🔧 Configuring AWS credentials...")
|
||||
if err := setupAWSCredentials(accessKey, secretKey, region, certPath); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Warning: Could not update AWS credentials: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("✅ AWS credentials configured")
|
||||
}
|
||||
|
||||
// Create additional directories
|
||||
dirs := []string{
|
||||
filepath.Join(hiveRoot, "images"),
|
||||
filepath.Join(hiveRoot, "amis"),
|
||||
filepath.Join(hiveRoot, "volumes"),
|
||||
filepath.Join(hiveRoot, "state"),
|
||||
filepath.Join(hiveRoot, "logs"),
|
||||
filepath.Join(hiveRoot, "nats"),
|
||||
filepath.Join(hiveRoot, "predastore"),
|
||||
filepath.Join(hiveRoot, "viperblock"),
|
||||
}
|
||||
|
||||
fmt.Println("\n📁 Creating directory structure...")
|
||||
for _, dir := range dirs {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Warning: Could not create %s: %v\n", dir, err)
|
||||
}
|
||||
}
|
||||
fmt.Printf("✅ Directory structure created in %s\n", hiveRoot)
|
||||
|
||||
// Print success message
|
||||
fmt.Println("\n🎉 Hive initialization complete!")
|
||||
fmt.Println("\n📋 Next steps:")
|
||||
fmt.Println(" 1. Start services:")
|
||||
fmt.Println(" ./scripts/start-dev.sh")
|
||||
fmt.Println()
|
||||
fmt.Println(" 2. Test with AWS CLI:")
|
||||
fmt.Println(" export AWS_PROFILE=hive")
|
||||
fmt.Println(" aws ec2 describe-instances")
|
||||
fmt.Println()
|
||||
fmt.Println("🔗 Configuration:")
|
||||
fmt.Printf(" Config file: %s\n", hiveTomlPath)
|
||||
fmt.Printf(" Data directory: %s\n", hiveRoot)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// generateAWSAccessKey generates an AWS-style access key
|
||||
// Format: AKIA + 16 random uppercase alphanumeric characters
|
||||
func generateAWSAccessKey() string {
|
||||
const prefix = "AKIA"
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
const length = 16
|
||||
|
||||
result := make([]byte, length)
|
||||
for i := range result {
|
||||
num, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
|
||||
result[i] = charset[num.Int64()]
|
||||
}
|
||||
|
||||
return prefix + string(result)
|
||||
}
|
||||
|
||||
// generateAWSSecretKey generates an AWS-style secret key
|
||||
// 40 character base64-encoded string
|
||||
func generateAWSSecretKey() string {
|
||||
bytes := make([]byte, 30) // 30 bytes = 40 chars in base64
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(bytes)
|
||||
}
|
||||
|
||||
// generateAccountID generates a 12-digit AWS account ID
|
||||
func generateAccountID() string {
|
||||
// Generate random 12-digit number
|
||||
num, _ := rand.Int(rand.Reader, big.NewInt(900000000000))
|
||||
accountID := num.Int64() + 100000000000 // Ensure it's 12 digits
|
||||
return fmt.Sprintf("%012d", accountID)
|
||||
}
|
||||
|
||||
// generateNATSToken generates a secure random token for NATS
|
||||
func generateNATSToken() string {
|
||||
bytes := make([]byte, 32)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return "nats_" + base64.URLEncoding.EncodeToString(bytes)[:32]
|
||||
}
|
||||
|
||||
// generateSelfSignedCert generates a self-signed SSL certificate
|
||||
func generateSelfSignedCert(certPath, keyPath string) error {
|
||||
// Generate private key
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate private key: %w", err)
|
||||
}
|
||||
|
||||
// Create certificate template
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(3650 * 24 * time.Hour) // 10 years
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate serial number: %w", err)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: "localhost",
|
||||
Organization: []string{"Hive Platform"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{"localhost"},
|
||||
IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")},
|
||||
}
|
||||
|
||||
// Create certificate
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create certificate: %w", err)
|
||||
}
|
||||
|
||||
// Write certificate to file
|
||||
certOut, err := os.Create(certPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create cert file: %w", err)
|
||||
}
|
||||
defer certOut.Close()
|
||||
|
||||
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
||||
return fmt.Errorf("failed to write cert: %w", err)
|
||||
}
|
||||
|
||||
// Write private key to file
|
||||
keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create key file: %w", err)
|
||||
}
|
||||
defer keyOut.Close()
|
||||
|
||||
privBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal private key: %w", err)
|
||||
}
|
||||
|
||||
if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
|
||||
return fmt.Errorf("failed to write key: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateConfigFile creates a configuration file from a template
|
||||
func generateConfigFile(path, templateContent, accessKey, secretKey, accountID, region, natsToken, dataDir string) error {
|
||||
// Parse the embedded template
|
||||
tmpl, err := template.New("config").Parse(templateContent)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse template: %w", err)
|
||||
}
|
||||
|
||||
// Template data
|
||||
data := struct {
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
AccountID string
|
||||
Region string
|
||||
NatsToken string
|
||||
DataDir string
|
||||
}{
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
AccountID: accountID,
|
||||
Region: region,
|
||||
NatsToken: natsToken,
|
||||
DataDir: dataDir,
|
||||
}
|
||||
|
||||
// Create file with secure permissions
|
||||
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Execute template
|
||||
if err := tmpl.Execute(f, data); err != nil {
|
||||
return fmt.Errorf("failed to execute template: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupAWSCredentials updates ~/.aws/credentials and ~/.aws/config
|
||||
func setupAWSCredentials(accessKey, secretKey, region, certPath string) error {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
awsDir := filepath.Join(homeDir, ".aws")
|
||||
if err := os.MkdirAll(awsDir, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
credPath := filepath.Join(awsDir, "credentials")
|
||||
configPath := filepath.Join(awsDir, "config")
|
||||
|
||||
// Determine profile name
|
||||
profileName := "default"
|
||||
if fileExists(credPath) {
|
||||
// Check if default profile already exists
|
||||
cfg, err := ini.Load(credPath)
|
||||
if err == nil && cfg.HasSection("default") {
|
||||
profileName = "hive"
|
||||
}
|
||||
}
|
||||
|
||||
// Update credentials file
|
||||
if err := updateAWSINIFile(credPath, profileName, map[string]string{
|
||||
"aws_access_key_id": accessKey,
|
||||
"aws_secret_access_key": secretKey,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update config file
|
||||
configSection := profileName
|
||||
if profileName != "default" {
|
||||
configSection = "profile " + profileName
|
||||
}
|
||||
|
||||
if err := updateAWSINIFile(configPath, configSection, map[string]string{
|
||||
"region": region,
|
||||
"endpoint_url": "https://localhost:9999",
|
||||
"ca_bundle": certPath,
|
||||
"output": "json",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf(" Profile: %s\n", profileName)
|
||||
if profileName != "default" {
|
||||
fmt.Printf(" Use: export AWS_PROFILE=%s\n", profileName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
func fileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// updateAWSINIFile updates or creates an AWS INI file section with given key-value pairs
|
||||
func updateAWSINIFile(path, section string, values map[string]string) error {
|
||||
var cfg *ini.File
|
||||
var err error
|
||||
|
||||
// Load existing file or create new one
|
||||
if fileExists(path) {
|
||||
cfg, err = ini.Load(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load INI file: %w", err)
|
||||
}
|
||||
} else {
|
||||
cfg = ini.Empty()
|
||||
}
|
||||
|
||||
// Get or create section
|
||||
sec, err := cfg.NewSection(section)
|
||||
if err != nil {
|
||||
// Section already exists, get it
|
||||
sec, err = cfg.GetSection(section)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get section: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set key-value pairs
|
||||
for key, value := range values {
|
||||
sec.Key(key).SetValue(value)
|
||||
}
|
||||
|
||||
// Save with proper permissions
|
||||
return cfg.SaveTo(path)
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/mulgadc/hive/hive/config"
|
||||
"github.com/mulgadc/hive/hive/service"
|
||||
"github.com/mulgadc/hive/hive/services/nats"
|
||||
@@ -430,6 +431,8 @@ var awsgwStartCmd = &cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Loading config from:", cfgFile)
|
||||
|
||||
// TODO: Support ENV vars, CLI, otherwise revert to config.LoadConfig()
|
||||
appConfig, err := config.LoadConfig(cfgFile)
|
||||
|
||||
@@ -458,8 +461,17 @@ var awsgwStartCmd = &cobra.Command{
|
||||
appConfig.AWSGW.TLSKey = awsgwTlsKey
|
||||
}
|
||||
|
||||
baseDir := viper.GetString("base-dir")
|
||||
|
||||
if awsgwTlsKey != "" {
|
||||
fmt.Println("Overwriting awsgw base-dir to:", baseDir)
|
||||
appConfig.BaseDir = baseDir
|
||||
}
|
||||
|
||||
service, err := service.New("awsgw", appConfig)
|
||||
|
||||
spew.Dump(appConfig)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Error starting awsgw service:", err)
|
||||
return
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Test configuration file in TOML format.
|
||||
# AWS Gateway configuration file in TOML format.
|
||||
version = "1.0"
|
||||
region = "ap-southeast-2"
|
||||
region = "{{.Region}}"
|
||||
|
||||
# Enable verbose debugging
|
||||
debug = false
|
||||
|
||||
[[auth]]
|
||||
access_key_id = "AKIAIOSFODNN7EXAMPLE"
|
||||
secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||
access_key_id = "{{.AccessKey}}"
|
||||
secret_access_key = "{{.SecretKey}}"
|
||||
policy = [
|
||||
{ bucket = "predastore", actions = ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListAllMyBuckets"] },
|
||||
]
|
||||
@@ -1,9 +1,10 @@
|
||||
# Domain configration file in TOML format.
|
||||
# Domain configuration file in TOML format.
|
||||
version = "1.0"
|
||||
region = "ap-southeast-2"
|
||||
region = "{{.Region}}"
|
||||
account_id = "{{.AccountID}}"
|
||||
|
||||
access_key = "AKIAIOSFODNN7EXAMPLE"
|
||||
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||
access_key = "{{.AccessKey}}"
|
||||
secret_key = "{{.SecretKey}}"
|
||||
|
||||
[daemon]
|
||||
host = "0.0.0.0:4432"
|
||||
@@ -15,18 +16,19 @@ host = "0.0.0.0:9999"
|
||||
tlskey = "config/server.key"
|
||||
tlscert = "config/server.pem"
|
||||
debug = true
|
||||
config = "awsgw/awsgw.toml"
|
||||
|
||||
[nats]
|
||||
host = "0.0.0.0:4222"
|
||||
|
||||
[nats.acl]
|
||||
token = "your-nats-token"
|
||||
token = "{{.NatsToken}}"
|
||||
|
||||
[nats.sub]
|
||||
|
||||
[predastore]
|
||||
host = "0.0.0.0:8443"
|
||||
region = "ap-southeast-2"
|
||||
region = "{{.Region}}"
|
||||
bucket = "predastore"
|
||||
access_key = "AKIAIOSFODNN7EXAMPLE"
|
||||
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||
access_key = "{{.AccessKey}}"
|
||||
secret_key = "{{.SecretKey}}"
|
||||
@@ -0,0 +1,32 @@
|
||||
# NATS Server Configuration
|
||||
# Generated by Hive admin init
|
||||
|
||||
# Server configuration
|
||||
port: 4222
|
||||
server_name: hive-nats
|
||||
|
||||
# Authorization
|
||||
authorization {
|
||||
token: "{{.NatsToken}}"
|
||||
}
|
||||
|
||||
# JetStream for persistent messaging
|
||||
jetstream {
|
||||
store_dir: "{{.DataDir}}/nats/storage"
|
||||
max_memory_store: 1GB
|
||||
max_file_store: 10GB
|
||||
}
|
||||
|
||||
# Logging
|
||||
debug: false
|
||||
trace: false
|
||||
logtime: true
|
||||
log_file: "{{.DataDir}}/logs/nats.log"
|
||||
|
||||
# Limits
|
||||
max_connections: 1000
|
||||
max_payload: 1MB
|
||||
max_pending: 64MB
|
||||
|
||||
# Monitoring
|
||||
http_port: 8222
|
||||
@@ -1,6 +1,6 @@
|
||||
# Test configuration file in TOML format.
|
||||
# Predastore S3-compatible storage configuration file in TOML format.
|
||||
version = "1.0"
|
||||
region = "ap-southeast-2"
|
||||
region = "{{.Region}}"
|
||||
|
||||
# Optionally configure port and host, or specify via daemon CLI options
|
||||
host = "0.0.0.0"
|
||||
@@ -14,15 +14,15 @@ debug = false
|
||||
|
||||
[[buckets]]
|
||||
name = "predastore"
|
||||
region = "ap-southeast-2"
|
||||
region = "{{.Region}}"
|
||||
type = "fs"
|
||||
pathname = "data/predastore"
|
||||
public = false
|
||||
encryption = ""
|
||||
|
||||
[[auth]]
|
||||
access_key_id = "AKIAIOSFODNN7EXAMPLE"
|
||||
secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||
access_key_id = "{{.AccessKey}}"
|
||||
secret_access_key = "{{.SecretKey}}"
|
||||
policy = [
|
||||
{ bucket = "predastore", actions = ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListAllMyBuckets"] },
|
||||
]
|
||||
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// netCmd represents the net command
|
||||
var netCmd = &cobra.Command{
|
||||
Use: "net",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("net called")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(netCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// netCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// netCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
Copyright © 2025 Mulga Defense Corporation
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var cfgFile string
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "hive",
|
||||
Short: "A brief description of your application",
|
||||
Long: `A longer description that spans multiple lines and likely contains
|
||||
examples and usage of using your application. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
// Uncomment the following line if your bare application
|
||||
// has an action associated with it:
|
||||
// Run: func(cmd *cobra.Command, args []string) { },
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
// Cobra supports persistent flags, which, if defined here,
|
||||
// will be global for your application.
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.hive.yaml)")
|
||||
|
||||
// Cobra also supports local flags, which will only run
|
||||
// when this action is called directly.
|
||||
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
|
||||
// initConfig reads in config file and ENV variables if set.
|
||||
func initConfig() {
|
||||
if cfgFile != "" {
|
||||
// Use config file from the flag.
|
||||
viper.SetConfigFile(cfgFile)
|
||||
} else {
|
||||
// Find home directory.
|
||||
home, err := os.UserHomeDir()
|
||||
cobra.CheckErr(err)
|
||||
|
||||
// Search config in home directory with name ".hive" (without extension).
|
||||
viper.AddConfigPath(home)
|
||||
viper.SetConfigType("yaml")
|
||||
viper.SetConfigName(".hive")
|
||||
}
|
||||
|
||||
viper.AutomaticEnv() // read in environment variables that match
|
||||
|
||||
// If a config file is found, read it in.
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// vmCmd represents the vm command
|
||||
var vmCmd = &cobra.Command{
|
||||
Use: "vm",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("vm called")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(vmCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// vmCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// vmCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// volumeCmd represents the volume command
|
||||
var volumeCmd = &cobra.Command{
|
||||
Use: "volume",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("volume called")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(volumeCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// volumeCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// volumeCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
Copyright © 2025 Mulga Defense Corporation
|
||||
|
||||
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 main
|
||||
|
||||
import "github.com/mulgadc/hive/cmd/hivectl/cmd"
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// Parse AWS Smithy models
|
||||
|
||||
type Smithy struct {
|
||||
Smithy string `json:"smithy"`
|
||||
Metadata map[string]interface{}
|
||||
Shapes map[string]interface{}
|
||||
}
|
||||
|
||||
type SmithyRange struct {
|
||||
Min int `json:"min"`
|
||||
Max int `json:"max"`
|
||||
}
|
||||
|
||||
type SmithyPaginated struct {
|
||||
InputToken string `json:"inputToken"`
|
||||
OutputToken string `json:"outputToken"`
|
||||
Items string `json:"items"`
|
||||
PageSize string `json:"pageSize"`
|
||||
}
|
||||
|
||||
type SmithyTrait struct {
|
||||
Documentation string `json:"smithy.api#documentation"`
|
||||
XMLName string `json:"smithy.api#xmlName"`
|
||||
Ec2QueryName string `json:"smithy.api#ec2QueryName"`
|
||||
|
||||
Range SmithyRange `json:"smithy.api#range"` // optional
|
||||
SmithyPaginated SmithyPaginated `json:"smithy.api#paginated"` // optional
|
||||
|
||||
}
|
||||
|
||||
type SmithyMember struct {
|
||||
Target string `json:"target"`
|
||||
Traits SmithyTrait `json:"traits"`
|
||||
}
|
||||
|
||||
type SmithyMemberStructure struct {
|
||||
Target string `json:"target"`
|
||||
Traits SmithyTrait `json:"traits"`
|
||||
}
|
||||
|
||||
type SmithyRequest struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type SmithyRequestList struct {
|
||||
Type string `json:"type"`
|
||||
Member SmithyMember `json:"member"`
|
||||
}
|
||||
|
||||
type SmithyRequestStructure struct {
|
||||
Type string `json:"type"`
|
||||
Members map[string]SmithyMemberStructure `json:"members"`
|
||||
Traits SmithyTrait `json:"traits"`
|
||||
}
|
||||
|
||||
var result Smithy
|
||||
|
||||
var smithyTypes = map[string]string{
|
||||
"String": "string",
|
||||
"Integer": "int",
|
||||
"Boolean": "bool",
|
||||
"DateTime": "time.Time",
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
path := flag.String("path", "", "Path to the Smithy model")
|
||||
flag.Parse()
|
||||
|
||||
if path == nil {
|
||||
log.Fatal("path is required")
|
||||
}
|
||||
|
||||
// Open the file and read JSON
|
||||
jsonFile, err := os.Open(*path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer jsonFile.Close()
|
||||
|
||||
// Read the file and unmarshal as JSON
|
||||
byteValue, _ := io.ReadAll(jsonFile)
|
||||
|
||||
json.Unmarshal([]byte(byteValue), &result)
|
||||
|
||||
// spew.Dump(result)
|
||||
|
||||
_, req := findShape("com.amazonaws.ec2#DescribeInstancesRequest")
|
||||
scanRequest("com.amazonaws.ec2#DescribeInstancesRequest", req, true)
|
||||
|
||||
/*
|
||||
for k, v := range result.Shapes {
|
||||
|
||||
// Find all request methods
|
||||
|
||||
// "com.amazonaws.ec2#RunInstancesRequest"
|
||||
// DescribeInstancesRequest
|
||||
if strings.HasSuffix(k, "DescribeInstancesRequest") {
|
||||
|
||||
//fmt.Println(k)
|
||||
|
||||
scanRequest(k, v.(interface{}), true)
|
||||
|
||||
// Loop in each request
|
||||
//for k2, v2 := range v.(map[string]interface{}) {
|
||||
// fmt.Println(k2, v2)
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
func scanRequest(key string, request interface{}, root bool) {
|
||||
|
||||
//spew.Dump(request)
|
||||
// Marshal into a SmithyRequest
|
||||
|
||||
// Unmarshal into a SmithyRequest
|
||||
|
||||
var smithyRequest SmithyRequest
|
||||
|
||||
b, _ := json.Marshal(request) // map -> JSON
|
||||
_ = json.Unmarshal(b, &smithyRequest) // JSON -> struct
|
||||
|
||||
//spew.Dump(smithyRequest)
|
||||
|
||||
keyName := strings.Split(key, "#")
|
||||
|
||||
if len(keyName) == 0 {
|
||||
fmt.Println("No key found")
|
||||
return
|
||||
}
|
||||
|
||||
if root {
|
||||
fmt.Printf("type %s struct {\n", keyName[1])
|
||||
}
|
||||
|
||||
if smithyRequest.Type == "structure" {
|
||||
findShapeRecursiveStructure(request)
|
||||
} else if smithyRequest.Type == "list" {
|
||||
findShapeRecursiveList(request)
|
||||
}
|
||||
|
||||
//fmt.Println(smithyRequest.Type)
|
||||
|
||||
//for k, v := range request {
|
||||
// fmt.Println(k, v)
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
func findShapeRecursiveList(request interface{}) error {
|
||||
|
||||
var smithyRequest SmithyRequestList
|
||||
|
||||
b, _ := json.Marshal(request) // map -> JSON
|
||||
_ = json.Unmarshal(b, &smithyRequest) // JSON -> struct
|
||||
|
||||
fmt.Println("findShapeRecursiveList:")
|
||||
spew.Dump(request)
|
||||
spew.Dump(smithyRequest)
|
||||
|
||||
memberType := strings.Split(smithyRequest.Member.Target, "#")
|
||||
|
||||
fmt.Println(memberType)
|
||||
|
||||
if len(memberType) == 0 {
|
||||
fmt.Println("No member type found")
|
||||
return nil
|
||||
}
|
||||
|
||||
typeName := memberType[1]
|
||||
|
||||
// If a matching smithyType
|
||||
goType := smithyTypes[typeName]
|
||||
|
||||
if goType != "" {
|
||||
fmt.Printf("%s `xml:\"%s\"`\n", goType, smithyRequest.Member.Traits.XMLName)
|
||||
|
||||
} else {
|
||||
//spew.Dump(v)
|
||||
fmt.Println("\t", smithyRequest.Member.Target, smithyRequest.Member.Traits.XMLName)
|
||||
|
||||
newSmithyRequest, request := findShape(smithyRequest.Member.Target)
|
||||
|
||||
if newSmithyRequest.Type == "structure" {
|
||||
findShapeRecursiveStructure(request)
|
||||
} else if newSmithyRequest.Type == "list" {
|
||||
findShapeRecursiveList(request)
|
||||
}
|
||||
|
||||
//findShapeRecursiveStructure(newSmithyRequest)
|
||||
|
||||
}
|
||||
|
||||
//fmt.Println(smithyRequest.Members)
|
||||
|
||||
fmt.Println()
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func findShapeRecursiveStructure(request interface{}) error {
|
||||
|
||||
fmt.Println("findShapeRecursiveStructure => \t")
|
||||
|
||||
var smithyRequest SmithyRequestStructure
|
||||
|
||||
b, _ := json.Marshal(request) // map -> JSON
|
||||
_ = json.Unmarshal(b, &smithyRequest) // JSON -> struct
|
||||
|
||||
for k, v := range smithyRequest.Members {
|
||||
|
||||
//fmt.Println("\t", k, v)
|
||||
|
||||
memberType := strings.Split(v.Target, "#")
|
||||
|
||||
fmt.Println(memberType)
|
||||
|
||||
if len(memberType) == 0 {
|
||||
fmt.Println("No member type found")
|
||||
continue
|
||||
}
|
||||
|
||||
typeName := memberType[1]
|
||||
|
||||
// If a matching smithyType
|
||||
goType := smithyTypes[typeName]
|
||||
|
||||
if goType != "" {
|
||||
fmt.Printf("%s %s `xml:\"%s\"`\n", k, goType, v.Traits.XMLName)
|
||||
|
||||
} else {
|
||||
//spew.Dump(v)
|
||||
//fmt.Println("\t", k, v.Target, v.Traits.XMLName)
|
||||
|
||||
newSmithyRequest, request := findShape(v.Target)
|
||||
|
||||
if newSmithyRequest.Type == "structure" {
|
||||
findShapeRecursiveStructure(request)
|
||||
} else if newSmithyRequest.Type == "list" {
|
||||
findShapeRecursiveList(request)
|
||||
}
|
||||
//findShapeRecursiveStructure(newSmithyRequest)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//fmt.Println(smithyRequest.Members)
|
||||
|
||||
fmt.Println()
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func findShape(key string) (SmithyRequest, interface{}) {
|
||||
|
||||
for k, v := range result.Shapes {
|
||||
if k == key {
|
||||
|
||||
//fmt.Println("MATCH", k, key)
|
||||
|
||||
//spew.Dump(v)
|
||||
//fmt.Println(k, v)
|
||||
var smithyRequest SmithyRequest
|
||||
|
||||
b, _ := json.Marshal(v) // map -> JSON
|
||||
_ = json.Unmarshal(b, &smithyRequest) // JSON -> struct
|
||||
|
||||
//spew.Dump(v)
|
||||
|
||||
//spew.Dump(smithyRequest)
|
||||
|
||||
return smithyRequest, v
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return SmithyRequest{}, nil
|
||||
|
||||
}
|
||||
@@ -16,6 +16,52 @@ This directory contains the systemd service configuration for running Hive servi
|
||||
2. Ensure the `hive` binary is available at `/opt/hive/bin/hive`
|
||||
3. Run the installation script as root
|
||||
|
||||
### Setup Configuration
|
||||
|
||||
```
|
||||
/opt/hive/bin/hive admin init
|
||||
```
|
||||
|
||||
This will init the Hive environment and create the default configuration files in the home directory, e.g `~/hive/`.
|
||||
|
||||
```
|
||||
/home/username/hive:
|
||||
amis config images logs nats predastore state viperblock volumes
|
||||
|
||||
/home/username/hive/amis:
|
||||
|
||||
/home/username/hive/config:
|
||||
awsgw hive.toml nats predastore server.key server.pem
|
||||
|
||||
/home/username/hive/config/awsgw:
|
||||
awsgw.toml
|
||||
|
||||
/home/username/hive/config/nats:
|
||||
nats.conf
|
||||
|
||||
/home/username/hive/config/predastore:
|
||||
predastore.toml
|
||||
|
||||
/home/username/hive/images:
|
||||
|
||||
/home/username/hive/logs:
|
||||
awsgw.log awsgw.pid hive.log hive.pid nats.log nats.pid predastore.log predastore.pid viperblock.log viperblock.pid
|
||||
|
||||
/home/username/hive/nats:
|
||||
|
||||
/home/username/hive/predastore:
|
||||
data
|
||||
|
||||
/home/username/hive/predastore/data:
|
||||
predastore
|
||||
|
||||
/home/ben/hive/state:
|
||||
|
||||
/home/ben/hive/viperblock:
|
||||
|
||||
/home/ben/hive/volumes:
|
||||
```
|
||||
|
||||
### Quick Installation
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<domain type='kvm'>
|
||||
<name>hive-vm-01</name>
|
||||
<memory unit='MiB'>2048</memory>
|
||||
<vcpu placement='static'>2</vcpu>
|
||||
|
||||
<os>
|
||||
<type arch='x86_64' machine='pc-q35-6.2'>hvm</type>
|
||||
<boot dev='hd'/>
|
||||
</os>
|
||||
|
||||
<features>
|
||||
<acpi/>
|
||||
<apic/>
|
||||
<vmport state='off'/>
|
||||
</features>
|
||||
|
||||
<devices>
|
||||
<emulator>/usr/bin/qemu-system-x86_64</emulator>
|
||||
|
||||
<!-- NBD Disk Configuration -->
|
||||
<disk type='network' device='disk'>
|
||||
<driver name='qemu' type='raw' cache='none' io='native'/>
|
||||
<source protocol='nbd' name='exportname'>
|
||||
<host name='127.0.0.1' port='10809'/>
|
||||
</source>
|
||||
<target dev='vda' bus='virtio'/>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
|
||||
</disk>
|
||||
|
||||
<!-- Network, Console, etc. -->
|
||||
<interface type='network'>
|
||||
<source network='default'/>
|
||||
<model type='virtio'/>
|
||||
</interface>
|
||||
|
||||
<serial type='pty'>
|
||||
<target port='0'/>
|
||||
</serial>
|
||||
|
||||
<console type='pty'>
|
||||
<target type='serial' port='0'/>
|
||||
</console>
|
||||
|
||||
<graphics type='vnc' port='-1' autoport='yes'/>
|
||||
</devices>
|
||||
</domain>
|
||||
@@ -1,28 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/zZGieclULKuD
|
||||
6XrAYY7UWqAxBOxURnzmQ+fPLjmuuC1PiVGRQV9HERvtUqe1aRN31k7uq/sIvd/j
|
||||
naiF2GbTnB6DslWVAishlcQ1X6qZ6har7HZq8hfiUUgqfe5hlJuOSTGjvZlJmH3F
|
||||
rqan5iW/LpEVVeu3KEFCpTOZ74vLGejUhw8bWAUbTVpErz9srXnQw6Vye0n+aRPC
|
||||
uFfSWdVY0iMp6beb8dkfhRP1+PVvSqFFd36Fk6VKz220/2G3WrJ6+y9Zj+1aZVHE
|
||||
1vsMROYIG6wXjykOuxdWjQuIwYWSVYegaRf//HCfrEE8F6GiwhthoUmwon737VTm
|
||||
JUZG7D7HAgMBAAECggEAAZ/qR4yL28t890gpdjTsgPvTAqdJeD3OZloVlZBXVTdO
|
||||
bocsUvDzAF6AIFkpfj9/Xucm5mE8FV2H2RnCYkjo88B/w3Xe9wcvBDAMjUtLE/Eo
|
||||
Edk3rf2LxjGLRn/QoggS7p1SryKsjxG+Gv4DLsyI7gCr2E7Q01sBb8YA793X+su3
|
||||
cHYbI+m1xHKMK3SgVgrSsyfjTxzOhn6J9c4DfQYBDcmRgOsGImJkQDedrc0DuSF3
|
||||
q6C4coCQuQizdYfaVQAQJD+higDhcpVIeLrO0XMePmDKWhGG5/kPmbV0vsfPmPxr
|
||||
TZpvIZqDhiAzACax6yaA7+F6QNyghQTE/N1M7A0w3QKBgQDvOWcyOaChau3Eio06
|
||||
xo6T83khHMEOTIznqdYXcIDqpAIXZlsmluvmmARlea5ctc2115x6+BfZ7UjSBPXM
|
||||
espCu/2HBI6+DDboA36qm5Hf2uReB4svouwenhdwdDuLz7Yyah0A8cpmmy1x6ZRT
|
||||
M2EwuvIYHE1dah5IAqW0vkqhYwKBgQDNQNntWmOFsWrdjl5zyzG99dM9jD9uLGZV
|
||||
IHsJZzDBQA+Dq25JNQghT2/iolHtcueMsPeO/B4paBtVNOMqEwk9z2G0P3PT8PJN
|
||||
+69wDlVbhyMXGtwlGN1nQq6OEirbdEqpwBWaNibXAg/6YCLL4QcY55Bqqpuh/YqN
|
||||
4GqcmMe8TQKBgQCeLp/dJxvfAHyeNFt0nOyUSi8zRPcnBFk7qiLFtv+Xp8E2RdlJ
|
||||
6Auum6B0lQRFi3Wtoxd2fGwRDwTS0rLRdfQeMn5O9CWK5zq7Kexs1ZecpPnSbzyK
|
||||
0Dk/eZvYIRNqvHsmVOI+yKLkPAHNy/znExdWCJeid7x0Y4IIba+ylw10LwKBgBJH
|
||||
3WELdOO6aoczH+x2Q4bktxy7OgcJA1A/pVWwECKWHwueICN7Cq9U0OVBicR5QHBC
|
||||
broYcGNFt3F+VWEw8tjDeLNCF4Hf6uTRLs3vKOEPBywOo17WeRmyUbOroI+NKv01
|
||||
c3cZrZnmwB+PomnAMA6iyTUG3vetikKao0QxQ1DdAoGBAMNw6wA9ivcWGwt6v5AB
|
||||
p7X5ychDqC9xR/6xrnp+yFtrNYr4K9huSO2qRdh88YEG2u3fUDcX5v4rSUULx2hg
|
||||
e0CicdDxtOJYAu3ZLVj/Cm9EN0sXW5xmvxXCJXjB90o5Njft//lL234PTNPqisp/
|
||||
ludFyCEl+UT1ky8E5bswZQ+O
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -1,21 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDazCCAlOgAwIBAgIUL0W8hP7iGr4njDS6vFO7jd4A0LgwDQYJKoZIhvcNAQEL
|
||||
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA0MjgwOTQzNDBaFw0zNDA1
|
||||
MDYwOTQzNDBaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQC/zZGieclULKuD6XrAYY7UWqAxBOxURnzmQ+fPLjmu
|
||||
uC1PiVGRQV9HERvtUqe1aRN31k7uq/sIvd/jnaiF2GbTnB6DslWVAishlcQ1X6qZ
|
||||
6har7HZq8hfiUUgqfe5hlJuOSTGjvZlJmH3Frqan5iW/LpEVVeu3KEFCpTOZ74vL
|
||||
GejUhw8bWAUbTVpErz9srXnQw6Vye0n+aRPCuFfSWdVY0iMp6beb8dkfhRP1+PVv
|
||||
SqFFd36Fk6VKz220/2G3WrJ6+y9Zj+1aZVHE1vsMROYIG6wXjykOuxdWjQuIwYWS
|
||||
VYegaRf//HCfrEE8F6GiwhthoUmwon737VTmJUZG7D7HAgMBAAGjUzBRMB0GA1Ud
|
||||
DgQWBBQt6t17ADSJtihmhKJULlI5JbrRITAfBgNVHSMEGDAWgBQt6t17ADSJtihm
|
||||
hKJULlI5JbrRITAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQC6
|
||||
jQs/iU3mlYR9Ql6L7gN8rzq50Dy0HN7MzeWOJ3KjPLB2g0tYuXvZcStjQ50BXXcT
|
||||
kKM5wzy4Pp5AYYXFajlXnDZXYi94200KRzi3Ml8Kmb3nQtFd4EFbDuWCUNp9aynE
|
||||
B2A9H/hmH91f0CozcSyK5jtvBg8bdsC4YiWxPAvWwtqrSPGwBEpn7r764mRkqjue
|
||||
8g2OwiV3kxTPejcHM5HcyG4qAo4Gkyl5IZ7m9jxKoVrI+dqD6/DV6xvhaIYb73Qe
|
||||
7+PJNfoJDlNinH8yhbwmG8/DK4e/BFVj9uB64vcmiZ1sUoUsh7ONh50zh+UZNwRf
|
||||
35ZDkBLxPq5qWMUPsjZg
|
||||
-----END CERTIFICATE-----
|
||||
@@ -52,5 +52,6 @@ require (
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -120,6 +120,8 @@ golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
+13
-10
@@ -30,6 +30,7 @@ type AWSGWConfig struct {
|
||||
Host string `mapstructure:"host"`
|
||||
TLSKey string `mapstructure:"tlskey"`
|
||||
TLSCert string `mapstructure:"tlscert"`
|
||||
Config string `mapstructure:"config"`
|
||||
|
||||
Debug bool `mapstructure:"debug"`
|
||||
}
|
||||
@@ -205,17 +206,19 @@ func LoadConfig(configPath string) (*Config, error) {
|
||||
return nil, fmt.Errorf("error unmarshaling config: %w", err)
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if config.NATS.Host == "" {
|
||||
return nil, fmt.Errorf("NATS host is required")
|
||||
}
|
||||
// Validate required fields (can be overwritten by env vars)
|
||||
/*
|
||||
if config.NATS.Host == "" {
|
||||
return nil, fmt.Errorf("NATS host is required")
|
||||
}
|
||||
|
||||
if config.AccessKey == "" {
|
||||
return nil, fmt.Errorf("access key is required")
|
||||
}
|
||||
if config.SecretKey == "" {
|
||||
return nil, fmt.Errorf("secret key is required")
|
||||
}
|
||||
if config.AccessKey == "" {
|
||||
return nil, fmt.Errorf("access key is required")
|
||||
}
|
||||
if config.SecretKey == "" {
|
||||
return nil, fmt.Errorf("secret key is required")
|
||||
}
|
||||
*/
|
||||
|
||||
// fmt.Println("Config: ", config)
|
||||
|
||||
|
||||
@@ -17,9 +17,10 @@ import (
|
||||
)
|
||||
|
||||
type GatewayConfig struct {
|
||||
Debug bool `json:"debug"`
|
||||
DisableLogging bool `json:"disable_logging"`
|
||||
NATSConn *nats.Conn // Shared NATS connection for service communication
|
||||
Debug bool `json:"debug"`
|
||||
DisableLogging bool `json:"disable_logging"`
|
||||
NATSConn *nats.Conn // Shared NATS connection for service communication
|
||||
Config string // Shared AWS Gateway config for S3 auth
|
||||
}
|
||||
|
||||
var supportedServices = map[string]bool{
|
||||
@@ -89,7 +90,7 @@ func (gw *GatewayConfig) SetupRoutes() *fiber.App {
|
||||
s3 := s3.New(&s3.Config{})
|
||||
|
||||
// TODO: Support env var for config path, and external IAM
|
||||
s3.ConfigPath = "config/awsgw/awsgw.toml"
|
||||
s3.ConfigPath = gw.Config
|
||||
|
||||
err := s3.ReadConfig()
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package awsgw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mulgadc/hive/hive/config"
|
||||
@@ -76,11 +78,17 @@ func launchService(config *config.Config) (err error) {
|
||||
|
||||
slog.Info("Connected to NATS server", "host", config.NATS.Host)
|
||||
|
||||
// Append Base dir if config has no leading path
|
||||
if config.BaseDir != "" && !strings.HasPrefix(config.AWSGW.Config, "/") {
|
||||
config.AWSGW.Config = fmt.Sprintf("%s/%s", config.BaseDir, config.AWSGW.Config)
|
||||
}
|
||||
|
||||
// Create gateway with NATS connection
|
||||
gw := gateway.GatewayConfig{
|
||||
Debug: config.AWSGW.Debug,
|
||||
DisableLogging: false,
|
||||
NATSConn: natsConn,
|
||||
Config: config.AWSGW.Config,
|
||||
}
|
||||
|
||||
app := gw.SetupRoutes()
|
||||
|
||||
+17
-55
@@ -14,7 +14,8 @@ NATS_PORT=4222
|
||||
PREDASTORE_PORT=8443
|
||||
HIVE_GATEWAY_PORT=9999
|
||||
DATA_DIR="$HOME/hive"
|
||||
CONFIG_DIR="$PROJECT_ROOT/config"
|
||||
# Use CONFIG_DIR environment variable if set, otherwise default to ~/hive/config
|
||||
CONFIG_DIR="${CONFIG_DIR:-$HOME/hive/config}"
|
||||
|
||||
echo "🚀 Setting up Hive development environment..."
|
||||
echo "Project root: $PROJECT_ROOT"
|
||||
@@ -105,59 +106,26 @@ for port_info in "${ports[@]}"; do
|
||||
fi
|
||||
done
|
||||
|
||||
# Generate TLS certificates for development
|
||||
# Build Hive first (needed for admin init)
|
||||
echo ""
|
||||
echo "🔐 Setting up TLS certificates..."
|
||||
echo "🔨 Building Hive..."
|
||||
cd "$PROJECT_ROOT"
|
||||
make build
|
||||
echo "✅ Hive built successfully"
|
||||
|
||||
if [[ ! -f "$CONFIG_DIR/server.pem" ]] || [[ ! -f "$CONFIG_DIR/server.key" ]]; then
|
||||
echo "📋 Generating self-signed certificate for development..."
|
||||
openssl req -x509 -newkey rsa:4096 -keyout "$CONFIG_DIR/server.key" -out "$CONFIG_DIR/server.pem" \
|
||||
-days 365 -nodes -subj "/C=US/ST=Dev/L=Development/O=Hive/CN=localhost" \
|
||||
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1,IP:::1"
|
||||
echo "✅ TLS certificates generated"
|
||||
# Initialize Hive configuration using admin init
|
||||
echo ""
|
||||
echo "🔐 Initializing Hive configuration..."
|
||||
|
||||
if [[ ! -f "$CONFIG_DIR/hive.toml" ]]; then
|
||||
echo "📋 Running hive admin init..."
|
||||
./bin/hive admin init --config-dir "$CONFIG_DIR"
|
||||
echo "✅ Hive configuration initialized"
|
||||
else
|
||||
echo "✅ TLS certificates already exist"
|
||||
echo "✅ Hive configuration already exists"
|
||||
echo " To re-initialize, run: ./bin/hive admin init --force"
|
||||
fi
|
||||
|
||||
# Create development configuration
|
||||
echo ""
|
||||
echo "⚙️ Creating development configuration..."
|
||||
|
||||
cat > "$CONFIG_DIR/dev.yaml" << EOF
|
||||
# Hive Development Configuration
|
||||
server:
|
||||
host: "0.0.0.0"
|
||||
port: $HIVE_GATEWAY_PORT
|
||||
tls:
|
||||
cert_file: "$CONFIG_DIR/server.pem"
|
||||
key_file: "$CONFIG_DIR/server.key"
|
||||
|
||||
nats:
|
||||
host: "nats://localhost:$NATS_PORT"
|
||||
acl:
|
||||
token: "dev-token"
|
||||
|
||||
services:
|
||||
predastore:
|
||||
host: "https://localhost:$PREDASTORE_PORT"
|
||||
access_key: "EXAMPLEKEY"
|
||||
secret_key: "EXAMPLEKEY"
|
||||
bucket: "predastore"
|
||||
region: "ap-southeast-2"
|
||||
|
||||
viperblock:
|
||||
base_dir: "$DATA_DIR/viperblock"
|
||||
|
||||
data:
|
||||
base_dir: "$DATA_DIR"
|
||||
|
||||
logging:
|
||||
level: "info"
|
||||
format: "json"
|
||||
EOF
|
||||
|
||||
echo "✅ Development configuration created at $CONFIG_DIR/dev.yaml"
|
||||
|
||||
# Build components
|
||||
echo ""
|
||||
echo "🔨 Building components..."
|
||||
@@ -182,12 +150,6 @@ else
|
||||
echo "⚠️ Predastore directory not found, skipping build"
|
||||
fi
|
||||
|
||||
# Build Hive
|
||||
echo "📦 Building Hive..."
|
||||
cd "$PROJECT_ROOT"
|
||||
make build
|
||||
echo "✅ Hive built successfully"
|
||||
|
||||
echo ""
|
||||
echo "🎉 Development environment setup complete!"
|
||||
echo ""
|
||||
|
||||
+12
-7
@@ -10,7 +10,9 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
MULGA_ROOT="$(cd "$PROJECT_ROOT/.." && pwd)"
|
||||
|
||||
# Configuration paths
|
||||
CONFIG_DIR="$PROJECT_ROOT/config"
|
||||
# Use CONFIG_DIR environment variable if set, otherwise default to ~/hive/config
|
||||
CONFIG_DIR="${CONFIG_DIR:-$HOME/hive/config}"
|
||||
echo "Using configuration directory: $CONFIG_DIR"
|
||||
DATA_DIR="$HOME/hive"
|
||||
LOGS_DIR="$DATA_DIR/logs"
|
||||
WAL_DIR="$DATA_DIR/hive"
|
||||
@@ -19,8 +21,14 @@ echo "🚀 Starting Hive development environment..."
|
||||
echo "Project root: $PROJECT_ROOT"
|
||||
echo "Data directory: $DATA_DIR"
|
||||
|
||||
# Create necessary directories
|
||||
mkdir -p "$DATA_DIR"/{predastore,viperblock,logs}
|
||||
# Confirm configuration directory exists
|
||||
if [ ! -d "$CONFIG_DIR" ]; then
|
||||
echo "⚠️ Configuration directory $CONFIG_DIR does not exist."
|
||||
echo "Please init the hive environment using the CLI."
|
||||
echo "hive admin init"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if [ -d "/mnt/ramdisk" ]; then
|
||||
|
||||
@@ -170,8 +178,6 @@ export HIVE_VIPERBLOCK_S3_BUCKET=predastore
|
||||
export HIVE_VIPERBLOCK_S3_REGION=ap-southeast-2
|
||||
export HIVE_VIPERBLOCK_PLUGIN_PATH=$NBD_PLUGIN_PATH
|
||||
export HIVE_NATS_HOST=0.0.0.0:4222
|
||||
export HIVE_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE
|
||||
export HIVE_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
||||
export HIVE_BASE_DIR=$VB_BASE_DIR
|
||||
|
||||
#VIPERBLOCK_CMD="air -c .air-viperblock.toml"
|
||||
@@ -179,8 +185,6 @@ VIPERBLOCK_CMD="./bin/hive service viperblock start"
|
||||
|
||||
#VIPERBLOCK_CMD="go run cmd/hive/main.go service viperblock start \
|
||||
# --nats-host 0.0.0.0:4222 \
|
||||
# --access-key AKIAIOSFODNN7EXAMPLE \
|
||||
# --secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY \
|
||||
# --base-dir $VB_BASE_DIR \
|
||||
# --plugin-path $NBD_PLUGIN_PATH"
|
||||
|
||||
@@ -211,6 +215,7 @@ echo "5️⃣. Starting AWS Gateway..."
|
||||
|
||||
unset HIVE_NATS_HOST
|
||||
unset HIVE_PREDASTORE_HOST
|
||||
export HIVE_BASE_DIR=$CONFIG_DIR
|
||||
export HIVE_CONFIG_PATH=$CONFIG_DIR/hive.toml
|
||||
export HIVE_AWSGW_HOST="0.0.0.0:9999"
|
||||
export HIVE_AWSGW_TLS_CERT=$CONFIG_DIR/server.pem
|
||||
|
||||
Reference in New Issue
Block a user