Updated config and admin init, awsgw tweaks and templates

This commit is contained in:
benduncan
2025-11-02 13:49:44 +11:00
parent f37d980ddb
commit d2c2ced79e
24 changed files with 787 additions and 920 deletions
+620
View File
@@ -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)
}
+12
View File
@@ -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}}"
+32
View File
@@ -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"] },
]
-202
View File
@@ -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.
-40
View File
@@ -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")
}
-88
View File
@@ -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())
}
}
-40
View File
@@ -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")
}
-40
View File
@@ -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")
}
-22
View File
@@ -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()
}
-300
View File
@@ -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
}
+46
View File
@@ -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
-46
View File
@@ -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>
-28
View File
@@ -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-----
-21
View File
@@ -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-----
+1
View File
@@ -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
)
+2
View File
@@ -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
View File
@@ -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)
+5 -4
View File
@@ -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()
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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