Skip to content

Commit

Permalink
oss install standalone mode
Browse files Browse the repository at this point in the history
  • Loading branch information
AdheipSingh committed Nov 30, 2024
1 parent f723da2 commit bbc8ead
Showing 1 changed file with 187 additions and 18 deletions.
205 changes: 187 additions & 18 deletions cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package cmd

import (
"bufio"
"bytes"
"context"
"encoding/base64"
"fmt"
"log"
"os"
Expand All @@ -10,30 +13,74 @@ import (
"strings"
"sync"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"k8s.io/client-go/restmapper"
"k8s.io/client-go/tools/clientcmd"
)

// InstallOssCmd deploys Parseable OSS
var InstallOssCmd = &cobra.Command{
Use: "oss",
Short: "Deploy Parseable OSS",
Example: "pb install oss",
RunE: func(cmd *cobra.Command, args []string) error {
printBanner()
// Prompt for Kubernetes context
_, err := k8s.PromptK8sContext()
if err != nil {
return fmt.Errorf(red+"Error prompting Kubernetes context: %w"+reset, err)
}

// Prompt user to enter a namespace with yellow color
// Prompt user for namespace
fmt.Print(yellow + "Enter the Kubernetes namespace for deployment: " + reset)
reader := bufio.NewReader(os.Stdin)
namespace, err := reader.ReadString('\n')
namespace, _ := reader.ReadString('\n')
namespace = strings.TrimSpace(namespace)

// Prompt for username
fmt.Print(yellow + "Enter the Parseable username: " + reset)
username, _ := reader.ReadString('\n')
username = strings.TrimSpace(username)

// Prompt for password
fmt.Print(yellow + "Enter the Parseable password: " + reset)
password, _ := reader.ReadString('\n')
password = strings.TrimSpace(password)

// Encode username and password to base64 for the Secret
encodedUsername := base64.StdEncoding.EncodeToString([]byte(username))
encodedPassword := base64.StdEncoding.EncodeToString([]byte(password))

// Prompt for deployment type
prompt := promptui.Select{
Label: "Select Deployment Type",
Items: []string{"Standalone", "Distributed"},
}
_, deploymentType, err := prompt.Run()
if err != nil {
return fmt.Errorf(red+"Error reading namespace input: %w"+reset, err)
log.Fatalf("Prompt failed: %v", err)
}

// Trim newline character and spaces from the input
namespace = strings.TrimSpace(namespace)
// Define Helm chart configuration based on deployment type
var chartValues []string
if deploymentType == "Standalone" {
chartValues = []string{
"parseable.image.repository=nikhilsinhaparseable/parseable",
"parseable.image.tag=debug-issue"}
} else {
chartValues = []string{"nikhilsinhaparseable/parseable=debug-issue"}
}

// Helm application configuration
apps := []helm.Helm{
{
ReleaseName: "parseable",
Expand All @@ -42,42 +89,164 @@ var InstallOssCmd = &cobra.Command{
RepoUrl: "https://charts.parseable.com",
ChartName: "parseable",
Version: "1.6.3",
Values: []string{"parseable.image.tag=v1.6.2"},
Values: chartValues,
},
}

// Create a WaitGroup to manage Go routines
var wg sync.WaitGroup
// Generate Kubernetes Secret manifest
secretManifest := fmt.Sprintf(`
apiVersion: v1
kind: Secret
metadata:
name: parseable-env-secret
namespace: %s
type: Opaque
data:
username: %s
password: %s
addr: %s
fs.dir: %s
staging.dir: %s
`, namespace, encodedUsername, encodedPassword, "MC4wLjAuMDo4MDAw", "Li9kYXRh", "Li9zdGFnaW5n")

// Use a channel to capture errors from Go routines
errCh := make(chan error, len(apps))
// Apply the Kubernetes Secret
if err := ApplyManifest(secretManifest); err != nil {
return fmt.Errorf(red+"Failed to create secret: %w"+reset, err)
}

// Deploy using Helm
var wg sync.WaitGroup
errCh := make(chan error, len(apps))
for _, app := range apps {
wg.Add(1)
go func(app helm.Helm) {
defer wg.Done() // Mark this Go routine as done when it finishes
defer wg.Done()
log.Printf("Deploying %s in namespace %s...", app.ReleaseName, app.Namespace)
if err := helm.Apply(app); err != nil {
log.Printf(red+"Failed to deploy %s: %v"+reset, app.ReleaseName, err)
errCh <- err // Send the error to the channel
errCh <- err
return
}
log.Printf(green+"%s deployed successfully."+reset, app.ReleaseName)
}(app) // Pass the app variable to the closure to avoid capturing issues
}(app)
}

// Wait for all Go routines to complete
wg.Wait()
close(errCh) // Close the error channel after all routines finish
close(errCh)

// Check for errors from Go routines
// Check for errors
for err := range errCh {
if err != nil {
return err // Return the first error encountered
return err
}
}

log.Println(green + "Parseable deployed successfully." + reset)
return nil
},
}

// ApplyManifest ensures the namespace exists and applies a Kubernetes manifest YAML to the cluster
func ApplyManifest(manifest string) error {
// Load kubeconfig and create a dynamic Kubernetes client
config, err := loadKubeConfig()
if err != nil {
return fmt.Errorf("failed to load kubeconfig: %w", err)
}

dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
return fmt.Errorf("failed to create dynamic client: %w", err)
}

// Parse the manifest YAML into an unstructured object
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 1024)
var obj unstructured.Unstructured
if err := decoder.Decode(&obj); err != nil {
return fmt.Errorf("failed to decode manifest: %w", err)
}

// Get the namespace from the manifest object
namespace := obj.GetNamespace()

if namespace != "" {
// Ensure the namespace exists, create it if it doesn't
namespaceClient := dynamic.NewForConfigOrDie(config).Resource(schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "namespaces",
})

// Try to get the namespace
_, err := namespaceClient.Get(context.TODO(), namespace, metav1.GetOptions{})
if err != nil {
// If namespace doesn't exist, create it
namespaceObj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": map[string]interface{}{
"name": namespace,
},
},
}
_, err := namespaceClient.Create(context.TODO(), namespaceObj, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("failed to create namespace %s: %w", namespace, err)
}
fmt.Printf("Namespace %s created successfully.\n", namespace)
}
}

// Get the GroupVersionResource dynamically
gvr, err := getGVR(config, &obj)
if err != nil {
return fmt.Errorf("failed to get GVR: %w", err)
}

// Apply the manifest using the dynamic client
_, err = dynamicClient.Resource(gvr).Namespace(namespace).Create(context.TODO(), &obj, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("failed to apply manifest: %w", err)
}

fmt.Println("Manifest applied successfully.")
return nil
}

// loadKubeConfig loads the kubeconfig from the default location
func loadKubeConfig() (*rest.Config, error) {
kubeconfig := clientcmd.NewDefaultClientConfigLoadingRules().GetDefaultFilename()
return clientcmd.BuildConfigFromFlags("", kubeconfig)
}

// getGVR fetches the GroupVersionResource for the provided object
func getGVR(config *rest.Config, obj *unstructured.Unstructured) (schema.GroupVersionResource, error) {
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
if err != nil {
return schema.GroupVersionResource{}, fmt.Errorf("failed to create discovery client: %w", err)
}

groupResources, err := restmapper.GetAPIGroupResources(discoveryClient)
if err != nil {
return schema.GroupVersionResource{}, fmt.Errorf("failed to get API group resources: %w", err)
}

mapper := restmapper.NewDiscoveryRESTMapper(groupResources)
gvk := obj.GroupVersionKind()
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return schema.GroupVersionResource{}, fmt.Errorf("failed to get GVR mapping: %w", err)
}

return mapping.Resource, nil
}

// printBanner displays a welcome banner
func printBanner() {
banner := `
--------------------------------------
Welcome to Parseable OSS Installation
--------------------------------------
`
fmt.Println(green + banner + reset)
}

0 comments on commit bbc8ead

Please sign in to comment.