From 5c129ca6057aef6d0f50ef612d7c443f6673ba81 Mon Sep 17 00:00:00 2001 From: Adel Zaalouk Date: Tue, 16 Jan 2024 21:31:14 +0100 Subject: [PATCH] Add discover mode --- README.md | 30 ++++++++++++++++++++++ discovery.go | 12 ++++----- main.go | 70 ++++++++++++++++++++++++++++------------------------ 3 files changed, 74 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 419725a..d5f5fc0 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ To use this tool, you need to have Go installed on your machine. Visit [Go's off 1. Clone the repository to your local machine. 2. Navigate to the cloned directory and build the tool with Go: +## Run in Manual Interactive Mode ```sh go build -o hcp-sizer ``` @@ -32,6 +33,35 @@ go build -o hcp-sizer 3. Run the calculator `./hcp-sizer` 4. Follow the interactive prompts to enter your cluster's specifications and choose the calculation method. + +## Run in Discovery Mode + +The HCP Sizer application has a special '_discover_' mode, which allows it to _discover_ needed input from a cluster directly. In this mode, the application will automatically fetch data from a Kubernetes cluster and perform sizing calculations without interactive prompts. This is particularly useful for continuous monitoring or periodic data fetching scenarios. + +### How to Enable Discovery Mode +To run the HCP Sizer in _discovery_ mode, use the -d or --discover flag when starting the application. Here's how you can do it: + +```sh +./hcp-sizer --discover +```` + +Or, using the shorthand flag version: + +```sh +./hcp-sizer -d +``` + +### What Happens in discovery Mode +When running in daemon mode, the application performs the following actions: + +* Initializes a connection to the Kubernetes cluster using the configured Kubernetes client. +* Fetches the current resource data (CPU, memory, and maximum pods) for nodes labeled as control-plane. +* Performs the sizing calculations based on the fetched data. +* Outputs the calculation results to the console. +* This process is done once, immediately after the application starts. +* Logging and Output + + # Use the release binaries diff --git a/discovery.go b/discovery.go index 30dfe2e..7a05478 100644 --- a/discovery.go +++ b/discovery.go @@ -50,7 +50,7 @@ type NodeResourceInfo struct { NodeName string CPU float64 // CPUs in the node Memory float64 // Memory in GiB - MaxPods int // Max Pods, needs custom logic for OpenShift + MaxPods int // Max Pods } func FetchClusterDataTwo(clientset *kubernetes.Clientset) ([]NodeResourceInfo, error) { @@ -68,8 +68,8 @@ func FetchClusterDataTwo(clientset *kubernetes.Clientset) ([]NodeResourceInfo, e cpu := node.Status.Allocatable.Cpu().MilliValue() memory := node.Status.Allocatable.Memory().Value() memoryInGiB := float64(memory) / (1024 * 1024 * 1024) - // Placeholder for maxPods logic - maxPods := inferMaxPodsFromNode(node) // Implement this function based on your setup + + maxPods := inferMaxPodsFromNode(node) nodesResourceInfo = append(nodesResourceInfo, NodeResourceInfo{ NodeName: node.Name, @@ -88,12 +88,12 @@ func inferMaxPodsFromNode(node corev1.Node) int { // The value is a Quantity, which needs to be parsed to an int maxPods, b := allocatablePods.AsInt64() if b != true { - // Handle error or use a default value if parsing fails - return 250 // Example default value + // Return the defult if the value cannot be parsed or if scale didn't happen + return 250 } return int(maxPods) } // Default value if the allocatable pods are not set - return 250 // Adjust this default value based on your needs + return 250 } diff --git a/main.go b/main.go index 1c154e0..4ff058f 100644 --- a/main.go +++ b/main.go @@ -50,12 +50,12 @@ func calculateETCDStorage(podCount float64) float64 { } func calculateMaxHCPs(workerCPUs, workerMemory, maxPods, apiRate float64, useLoadBased bool) float64 { - // print all values for debugging + //print all values for debugging fmt.Printf("workerCPUs: %f\n", workerCPUs) fmt.Printf("workerMemory: %f\n", workerMemory) fmt.Printf("maxPods: %f\n", maxPods) fmt.Printf("apiRate: %f\n", apiRate) - fmt.Printf("useLoadBased: %t\n", useLoadBased) + fmt.Printf("Using Load-based: %t\n", useLoadBased) maxHCPsByCPU := workerCPUs / cpuRequestPerHCP maxHCPsByMemory := workerMemory / memoryRequestPerHCP @@ -118,22 +118,48 @@ func promptForSelection(promptLabel string, items []string) int { return -1 } +var discoverMode bool + +func init() { + rootCmd.PersistentFlags().BoolVarP(&discoverMode, "discover", "d", false, "Run the application in discover mode") +} + var rootCmd = &cobra.Command{ Use: "hcp-sizer", Short: "An HCP Sizing Calculator based on Science!", Run: func(cmd *cobra.Command, args []string) { resources := ServerResources{} + if discoverMode { + clientset, err := InitializeKubernetesClientForExternalUse() + if err != nil { + fmt.Println("Failed to initialize Kubernetes client:", err) + os.Exit(1) + } - resources.WorkerCPUs = promptForInput("Enter the number of vCPUs on the worker node") - resources.WorkerMemory = promptForInput("Enter the memory (in GiB) on the worker node") - resources.MaxPods = promptForInput("Enter the maximum number of pods on the worker node (usually 250 or 500)") - resources.PodCount = promptForInput("Enter the number of pods you plan to run on your cluster (for ETCD storage calculation)") - resources.CalculationMethod = promptForSelection("Select Calculation Method", []string{"Request-Based", "Load-Based"}) - resources.UseLoadBased = resources.CalculationMethod == 1 + nodeResources, err := FetchClusterDataTwo(clientset) + if err != nil { + fmt.Println("Failed to fetch data from Kubernetes cluster:", err) + os.Exit(1) + } + // for simplicity, let's pick the first node we see + resources.WorkerCPUs = nodeResources[0].CPU + resources.WorkerMemory = nodeResources[0].Memory + resources.MaxPods = float64(nodeResources[0].MaxPods) + resources.PodCount = promptForInput("Enter the number of pods you plan to run on your cluster (for ETCD storage calculation)") + resources.CalculationMethod = promptForSelection("Select Calculation Method", []string{"Request-Based", "Load-Based"}) + resources.UseLoadBased = resources.CalculationMethod == 1 + } else { + // add flag for command + resources.WorkerCPUs = promptForInput("Enter the number of vCPUs on the worker node") + resources.WorkerMemory = promptForInput("Enter the memory (in GiB) on the worker node") + resources.MaxPods = promptForInput("Enter the maximum number of pods on the worker node (usually 250 or 500)") + resources.PodCount = promptForInput("Enter the number of pods you plan to run on your cluster (for ETCD storage calculation)") + resources.CalculationMethod = promptForSelection("Select Calculation Method", []string{"Request-Based", "Load-Based"}) + resources.UseLoadBased = resources.CalculationMethod == 1 + } // Check evaluation method, request-based or load-based (request is the more generic method) // load-based is preferred when data about QPS is available (e.g. from an existing cluster) - if resources.UseLoadBased { green := color.New(color.FgGreen) @@ -153,34 +179,14 @@ var rootCmd = &cobra.Command{ // Print the results italitYellow.Printf("Maximum HCPs that can be hosted: %.2f\n", math.Floor(resources.MaxHCPs)) italitYellow.Printf("Estimated HCP ETCD Storage Requirement: %.3f GiB\n", resources.EtcdStorage) + }, } func main() { - //if err := rootCmd.Execute(); err != nil { - // fmt.Println(err) - // os.Exit(1) - //} - - clientset, err := InitializeKubernetesClientForExternalUse() - if err != nil { - fmt.Println("Failed to initialize Kubernetes client:", err) - os.Exit(1) - } - - resources, err := FetchClusterDataTwo(clientset) - if err != nil { - fmt.Println("Failed to fetch data from Kubernetes cluster:", err) + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) os.Exit(1) } - // for simplicity, let's pick the first node we see - resources = []NodeResourceInfo{resources[0]} - for _, resource := range resources { - fmt.Printf("NodeName: %s\n", resource.NodeName) - fmt.Printf("CPU: %f\n", resource.CPU) - fmt.Printf("Memory: %f\n", resource.Memory) - fmt.Printf("MaxPods: %d\n", resource.MaxPods) - } - }