diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06b73a145..76c73f943 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -108,33 +108,18 @@ The core Komiser Engine is written in Go (Golang) and leverages Go Modules. Here 2. 🔧 **GOPATH**: - Ensure that the **`GOPATH`** environment variable is configured appropriately. ---- - -## 🛠️ Komiser Installation - -### **Step 1: Installing Komiser CLI** -Follow the instructions in the [documentation](https://docs.komiser.io/getting-started/installation) to install the **Komiser CLI** for your operating system. - -### **Step 2: Connect to a Cloud Account** -To deploy a **self-hosted (local) instance** of Komiser, connect your Komiser CLI to a cloud account of your choice. Refer to the documentation of the [supported cloud providers](https://docs.komiser.io/configuration/cloud-providers/aws). - -### **Step 3: Accessing the Komiser UI** -Access the dashboard UI at **`http://localhost:3002`** once the local Komiser instance is running. - -![komiser-dashboard](https://hackmd.io/_uploads/Syo0bMtgT.png) - ---- ## 🌟 Ways to Contribute to Komiser Engine Komiser is an open-source cloud-agnostic resource manager. It helps you break down cloud resource costs at the resource level. As a cloud-agnostic cloud management tool, we always have more providers and cloud services to add, update, and cost-calculate. -### 1️⃣ Adding a new Cloud Provider -- Step 1: Create **`provider_name.go`** in **`providers/provider_name`** directory. +### ☁️ Adding a new Cloud Provider -- Step 2: Add the following boilerplate: +#### 1️⃣ Create provider. +Create `provider_name.go` in `providers/provider_name` directory. +#### 2️⃣ Add the following boilerplate: ```go package PROVIDER_NAME @@ -164,7 +149,8 @@ func FetchProviderData(ctx context.Context, client ProviderClient, db *bun.DB) { } ``` -- Step 3: Add SDK client details in [**`providers/provider.go`**](https://github.com/tailwarden/komiser/blob/develop/providers/providers.go): +#### 3️⃣ Add SDK client details: +Add your client details to [**`providers/provider.go`**](https://github.com/tailwarden/komiser/blob/develop/providers/providers.go) ```go type ProviderClient struct { @@ -188,7 +174,8 @@ type AzureClient struct { } ``` -- **Step 4:** Add provider configuration in TOML format in **`config.toml`**: +#### 4️⃣ Add provider configuration: +Add provider configuration in TOML format in **`config.toml`** ```toml [[gcp]] @@ -198,27 +185,27 @@ source="ENVIRONMENT_VARIABLES" profile="production" ``` -- **Step 5:** Compile a new Komiser binary: - +#### 5️⃣ Compile a new Komiser binary: ```bash go build ``` -- **Step 6:** Start a new Komiser development server: +#### 6️⃣ Start a new Komiser development server: ```bash ./komiser start ``` -### 2️⃣ Adding a new Cloud Service/Resource +### 🔋 Adding a new Cloud Service/Resource Here are the general steps to add a new service/resource for a cloud provider in Komiser: -**Step 1:** -Create a new file **`servicename.go`** under the path **`providers/provider_name/servicename`** +#### 1️⃣ Create Service +Create a new file `servicename.go` under the path `providers/provider_name/servicename` -**Step 2:** +#### 2️⃣ Add boilerplate Add the following boilerplate code, which defines the structure of any new service/resource to be added for a cloud provider: + ```go package service @@ -243,10 +230,10 @@ func MyServiceResources(ctx context.Context, client ProviderClient) ([]Resource, To understand how to write the required logic, you may refer any [existing examples](https://github.com/tailwarden/komiser/tree/develop/providers/aws) for inspiration! -**Step 3:** -Call the **`MyServiceResources()`** function from the above file, by adding it to **`providers/providername/provider.go`** file's **`listOfSupportedServices()`** function. +#### 3️⃣ Edit Provider +Call the `MyServiceResources()` function from the above file, by adding it to `providers/providername/provider.go` file's `listOfSupportedServices()` function. -``` +```go func listOfSupportedServices() []providers.FetchDataFunction { return []providers.FetchDataFunction{ ec2.Instances, @@ -267,15 +254,40 @@ func listOfSupportedServices() []providers.FetchDataFunction { . ``` -**Step 4:** -Repeat steps **`4,5,6`** accordingly and you'll see a new resource/service added to Komiser, in the dashboard! +#### 4️⃣ +Do above mentioned steps [4](#4️⃣-add-provider-configuration), [5](#5️⃣-compile-a-new-komiser-binary) and [6](#6️⃣-start-a-new-komiser-development-server). You'll see a new resource/service added to Komiser, in the dashboard! Additionally, [here](https://youtu.be/Vn5uc2elcVg?feature=shared) is a video tutorial of the entire process for your reference. +> 💡 Tip: you can also start the server via `go run *.go start --config ./config.toml` if you do want to skip the compile step! + ### 3️⃣ Enhance existing Cloud service/resource **So, you wish to improve the code quality of an existing cloud service/resource?** Feel free to discuss your ideas with us on our [Discord Server](https://discord.tailwarden.com) and [open a new issue](https://github.com/tailwarden/komiser/issues). +## 🧪 Testing Your Changes + +We leverage the [testing](https://pkg.go.dev/testing) package for tests. Test names follow the `TestXxx(*testing.T)` format where Xxx does not start with a lowercase letter. The function name serves to identify the test routine. +For creating a new test you create a `[name]_test.go` next to the file you'd like to test and replace `[name]` with your filename of the implementation. Look at any of the `*_test.go` files for an example or read the [official docs](https://pkg.go.dev/testing). +You then can run it with `go test /path/to/your/folder/where/the/test/is`. You can run all of our engine tests with `make tests`. You should see something similar to this: + +```logtalk +go test ./... | grep -v /dashboard/ +... +ok github.com/tailwarden/komiser/internal (cached) [no tests to run] +? github.com/tailwarden/komiser/providers/aws/ecr [no test files] +? github.com/tailwarden/komiser/providers/aws/ecs [no test files] +? github.com/tailwarden/komiser/providers/aws/efs [no test files] +? github.com/tailwarden/komiser/providers/aws/eks [no test files] +? github.com/tailwarden/komiser/providers/aws/elasticache [no test files] +? github.com/tailwarden/komiser/providers/aws/elb [no test files] +? github.com/tailwarden/komiser/providers/aws/iam [no test files] +ok github.com/tailwarden/komiser/providers/aws/ec2 (cached) +? github.com/tailwarden/komiser/providers/aws/kms [no test files] +? github.com/tailwarden/komiser/providers/aws/lambda [no test file +... +``` + # 🚀 Contributing to Komiser Dashboard UI Komiser Dashboard utilizes a modern tech stack. Here's a brief about it: diff --git a/Makefile b/Makefile index 5489e56cc..eb45309ef 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ package: ## test: Run tests. test: - go test -v $(go list ./... | grep -v /dashboard/) + go test ./... | grep -v /dashboard/ ## version: Show version. version: diff --git a/cmd/config.go b/cmd/config.go index 33163c86c..f50eebdb2 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -16,7 +16,7 @@ var configCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { c := models.Config{ AWS: []models.AWSConfig{ - models.AWSConfig{ + { Name: "Demo", Source: "CREDENTIALS_FILE", Profile: "default", diff --git a/dashboard/README.md b/dashboard/README.md index 1b423fcb3..5c54c0f90 100644 --- a/dashboard/README.md +++ b/dashboard/README.md @@ -20,7 +20,7 @@ Follow the [Contribution Guide](https://github.com/tailwarden/komiser/blob/devel From the Komiser root folder start the Komiser server by running: ```shell -go run \*.go start --config /path/to/config.toml +go run *.go start --config /path/to/config.toml ``` In a different terminal tab navigate to the `/dashboard` folder: diff --git a/dashboard/components/explorer/DependencyGraph.tsx b/dashboard/components/explorer/DependencyGraph.tsx index ccf97c6de..6b115e14e 100644 --- a/dashboard/components/explorer/DependencyGraph.tsx +++ b/dashboard/components/explorer/DependencyGraph.tsx @@ -1,7 +1,7 @@ /* eslint-disable react-hooks/exhaustive-deps */ import React, { useState, memo, useEffect } from 'react'; import CytoscapeComponent from 'react-cytoscapejs'; -import Cytoscape, { EventObject } from 'cytoscape'; +import Cytoscape, { EdgeSingular, EventObject } from 'cytoscape'; import popper from 'cytoscape-popper'; import nodeHtmlLabel, { @@ -91,6 +91,7 @@ const DependencyGraph = ({ data }: DependencyGraphProps) => { const content = document.createElement('div'); content.classList.add('popper-div'); content.innerHTML = event.target.data('label'); + content.style.pointerEvents = 'none'; document.body.appendChild(content); return content; @@ -108,6 +109,20 @@ const DependencyGraph = ({ data }: DependencyGraphProps) => { // Hide labels when being zoomed out cy.on('zoom', event => { + if (cy.zoom() <= zoomLevelBreakpoint) { + interface ExtendedEdgeSingular extends EdgeSingular { + popperRefObj?: any; + } + + // Check if a tooltip is present and remove it + cy.edges().forEach((edge: ExtendedEdgeSingular) => { + if (edge.popperRefObj) { + edge.popperRefObj.state.elements.popper.remove(); + edge.popperRefObj.destroy(); + } + }); + } + const opacity = cy.zoom() <= zoomLevelBreakpoint ? 0 : 1; Array.from( @@ -126,7 +141,7 @@ const DependencyGraph = ({ data }: DependencyGraphProps) => { return (
- { } ]} cy={(cy: Cytoscape.Core) => cyActionHandlers(cy)} - /> + /> */} {dataIsEmpty ? ( <>
diff --git a/dashboard/components/explorer/filter/DependendencyGraphFilter.tsx b/dashboard/components/explorer/filter/DependendencyGraphFilter.tsx index 5a1dd6624..906f85ef2 100644 --- a/dashboard/components/explorer/filter/DependendencyGraphFilter.tsx +++ b/dashboard/components/explorer/filter/DependendencyGraphFilter.tsx @@ -32,7 +32,7 @@ function DependendencyGraphFilter({ {!hasFilters ? ( <>
diff --git a/dashboard/components/feedback-widget/FeedbackWidget.tsx b/dashboard/components/feedback-widget/FeedbackWidget.tsx index 97e659925..23ecb3567 100644 --- a/dashboard/components/feedback-widget/FeedbackWidget.tsx +++ b/dashboard/components/feedback-widget/FeedbackWidget.tsx @@ -1,5 +1,4 @@ import { useState, useRef, useCallback, memo, SyntheticEvent } from 'react'; -import { FileUploader } from 'react-drag-drop-files'; // eslint-disable-next-line import/no-extraneous-dependencies import { toBlob } from 'html-to-image'; import Image from 'next/image'; @@ -8,8 +7,9 @@ import Modal from '@components/modal/Modal'; import Input from '@components/input/Input'; import settingsService from '@services/settingsService'; import Button from '@components/button/Button'; -import Toast from '@components/toast/Toast'; import { useToast } from '@components/toast/ToastProvider'; +import Toast from '@components/toast/Toast'; +import Upload from '@components/upload/Upload'; // We define the placeholder here for convenience // It's difficult to read when passed inline @@ -29,9 +29,7 @@ Outcome const useFeedbackWidget = (defaultState: boolean = false) => { const [showFeedbackModel, setShowFeedbackModal] = useState(defaultState); - const FILE_TYPES = ['JPG', 'PNG', 'GIF', 'TXT', 'LOG', 'MP4', 'AVI', 'MOV']; const FEEDBACK_MODAL_ID = 'feedback-modal'; - const MAX_FILE_SIZE_MB = 37; function openFeedbackModal() { setShowFeedbackModal(true); @@ -151,7 +149,7 @@ const useFeedbackWidget = (defaultState: boolean = false) => { } } - function uploadFile(attachement: File) { + function uploadFile(attachement: File | null): void { setFileAttachement(attachement); } @@ -198,7 +196,7 @@ const useFeedbackWidget = (defaultState: boolean = false) => { <>
takeScreenshot()} - className="w-[50%] grow cursor-pointer rounded border-2 border-black-170 py-5 text-center text-xs transition hover:border-[#B6EAEA] hover:bg-black-100" + className="flex-1 grow cursor-pointer rounded border-2 border-black-170 py-5 text-center text-xs transition hover:border-[#B6EAEA] hover:bg-black-100" > { )} - - showToast({ - hasError: true, - title: 'File upload failed', - message: err - }) - } - onSizeError={(err: string) => - showToast({ - hasError: true, - title: 'File upload failed', - message: err - }) - } - dropMessageStyle={{ - width: '100%', - height: '100%', - position: 'absolute', - background: '#F4F9F9', - top: 0, - right: 2, - display: 'flex', - flexGrow: 2, - opacity: 1, - zIndex: 20, - color: '#008484', - fontSize: 14, - border: 'none' - }} - > - {fileAttachement === null && ( -
- - - - - - -

- Drag and drop or{' '} - - choose a file - -

-
- )} - {fileAttachement !== null && ( - - )} - +
+ setFileAttachement(null)} + disabled={ + fileAttachement !== null || + isSendingFeedback || + isTakingScreenCapture + } + onTypeError={(err: string) => + showToast({ + hasError: true, + title: 'File upload failed', + message: err + }) + } + onSizeError={(err: string) => + showToast({ + hasError: true, + title: 'File upload failed', + message: err + }) + } + /> +
@@ -448,7 +290,12 @@ const useFeedbackWidget = (defaultState: boolean = false) => { .

-