From 26f13a47634327ddeb63752f3b27aa92ff70e699 Mon Sep 17 00:00:00 2001 From: Azanul Haque <42029519+Azanul@users.noreply.github.com> Date: Wed, 4 Oct 2023 14:46:26 +0530 Subject: [PATCH 001/113] Update README.md --- README.md | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 3848e9fba..9a80538cd 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@

Amp Logo

+Komiser is an open-source cloud-agnostic resource manager designed to analyze and manage cloud cost, usage, security, and governance all in one place. It integrates seamlessly with multiple cloud providers, including AWS, Azure, Civo, Digital Ocean, OCI, Linode, Tencent, Scaleway and [more](#supported-cloud-providers). +

Discord | Tailwarden Cloud | @@ -25,7 +27,7 @@ [![Price](https://img.shields.io/badge/price-FREE-0098f7.svg)](https://github.com/tailwarden/komiser/blob/master/LICENSE) [![Docker Stars](https://img.shields.io/docker/pulls/mlabouardy/komiser.svg)](https://hub.docker.com/r/mlabouardy/komiser) [![ELv2 License](https://img.shields.io/badge/license-ELv2-green)](LICENSE) [![Docker Stars](https://img.shields.io/github/issues/tailwarden/komiser.svg)](https://github.com/tailwarden/komiser/issues) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.tailwarden.com/) -Komiser is an open-source cloud-agnostic resource manager. It integrates with multiple cloud providers (including AWS, Azure, Civo, Digital Ocean, OCI, Linode, Tencent and Scaleway), builds a cloud asset inventory, and helps you break down your cost at the resource level ๐Ÿ’ฐ +

Komiser gif

@@ -72,7 +74,7 @@ brew install komiser -# What is Komiser? ๐Ÿคท +## What is Komiser? ๐Ÿคท Komiser is an open source project created to **analyse** and **manage cloud cost**, **usage**, **security** and **governance** all in one place. With komiser you can also: * Build an inventory of your cloud infrastructure assets. * Control your **resource usage** and gain visibility across all used services to achieve maximum cost-effectiveness. @@ -85,6 +87,10 @@ Komiser is an open source project created to **analyse** and **manage cloud cost ## Who is using it? Komiser was built with every Cloud Engineer, Developer, DevOps engineer and SRE in mind. We understand that tackling cost savings, security improvements and resource usage analyse efforts can be hard, sometimes just knowing where to start, can be the most challenging part at times. Komiser is here to help those cloud practitioners see their cloud resources and accounts much more clearly. Only with clear insight can timely and efficient actions take place. +## Supported cloud providers + + + # Getting started ๐Ÿ‘‡ ## Download @@ -107,7 +113,7 @@ Watch the installation [video here](https://www.youtube.com/watch?v=4veDmJpui44& ## Installation on Azure -Connect a local deployment of Komiser CLI to you [**Azure**](https://docs.komiser.io/docs/cloud-providers/azure?utm_source=github&utm_medium=social) account. +Connect a local deployment of Komiser CLI to you [**Azure**](https://docs.komiser.io/configuration/cloud-providers/azure?utm_source=github&utm_medium=social) account. ## Installation on GCP @@ -139,13 +145,13 @@ Connect a local deployment of Komiser CLI to you [**Tencent**](https://docs.komi Connect a local deployment of Komiser CLI to you [**Scaleway**](https://docs.komiser.io/docs/cloud-providers/scaleway?utm_source=github&utm_medium=social) account. -# Documentation ๐Ÿ“– +## Documentation ๐Ÿ“– Head over to the official `Komiser` documentation at [docs.komiser.io](https://docs.komiser.io?utm_source=github&utm_medium=social). The source repository for the documentation website is [tailwarden/docs.komiser.io](https://github.com/tailwarden/docs.komiser.io). We know that writing docs isn't usually at the top of too many peoples "What I like to do for fun" list, but if you feel so inclined, by all means, consider [contributing](https://docs.komiser.io/docs/contributing/contribute?utm_source=github&utm_medium=social) to our documentation repository, we will be very grateful. It's built using [Docusaurus](https://docusaurus.io/). -## Jump right in: +### Jump right in: * [Documentation overview](https://docs.komiser.io/docs/intro?utm_source=github&utm_medium=social) * [Installation](https://docs.komiser.io/docs/introduction/getting-started?utm_source=github&utm_medium=social) * [FAQs](https://docs.komiser.io/docs/faqs?utm_source=github&utm_medium=social) @@ -153,33 +159,37 @@ We know that writing docs isn't usually at the top of too many peoples "What I l * [How to: Komiser](https://www.youtube.com/watch?v=9pCimmIT-HQ&list=PLFIcIMmOFDZeMzcvOi7bPd4I6xUNq3A5R/alerts) * [Installation videos](https://www.youtube.com/watch?v=urxi9z2IUf4&list=PLFIcIMmOFDZfaO_WmUF_qnF8akCII7Uk_) -# Bugs and feature requests ๐Ÿž +## Bugs and feature requests ๐Ÿž Have a bug or a feature request? Please first read the issue guidelines and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/tailwarden/komiser/issues). -# Roadmap and Contributing ๐Ÿ›ฃ๏ธ +## Roadmap and Contributing ๐Ÿ›ฃ๏ธ We are very excited about what is in store in the coming weeks and months, take a look at the [public roadmap](https://roadmap.tailwarden.com/) to stay on top of what's coming down the pipeline. -Komiser is written in `Golang` and is `Elv2 licensed` - contributions are always welcome whether that means providing feedback, be it through GitHub, through the `#feedback` channel on our [Discord server](https://discord.tailwarden.com) or testing existing and new features. Feel free to check out our [contributor guidelines](./CONTRIBUTING.md) and consider becoming a **contributor** today. +Komiser is written in `Golang` and is `Elv2 licensed` - contributions are always welcome whether that means providing feedback through GitHub, through the `#feedback` channel on our [Discord server](https://discord.tailwarden.com), testing existing features or suggesting new ones. Feel free to check out our [contributor guidelines](./CONTRIBUTING.md) and consider becoming a **contributor** today. Learn how to contribute with these walkthrough videos: - [How to contribute to Komiser engine](https://www.youtube.com/watch?v=Vn5uc2elcVg) - [How to contribute to Komiser dashboard](https://www.youtube.com/watch?v=uwxj11-eRt8) -# Users ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ +## Users ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ If you'd like to have your company represented and are using `Komiser` please give formal written permission below via email to contact@tailwarden.com. We will need a URL to an SVG or png logo, a text title, and a company URL. -# Versioning ๐Ÿงฎ - -We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/tailwarden/komiser/tags). - -# Contributors +## Contributors + +## Versioning ๐Ÿงฎ + +We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/tailwarden/komiser/tags). + +## License + +Komiser is licensed under the terms of [Elastic License 2.0 (ELv2)](https://github.com/tailwarden/komiser/blob/release/LICENSE). From 96f4a2e0a3e7da620f58e6570a36b906ed40a87c Mon Sep 17 00:00:00 2001 From: Kunal Verma Date: Wed, 4 Oct 2023 18:35:38 +0530 Subject: [PATCH 002/113] feat:update-contribute-guide --- CONTRIBUTING.md | 360 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 322 insertions(+), 38 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd4c8d824..a1bec2b2c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,32 +1,145 @@ -We are always thrilled to receive pull requests, and do our best to process them as fast as possible. Not sure if that typo is worth a pull request? Do it! We will appreciate it. +# Komiser Contributing Guide -`Note: If your pull request is not accepted on the first try, donโ€™t be discouraged! If thereโ€™s a problem with the implementation, hopefully you received feedback on what to improve.` +## Welcome + +**Want to contribute to Komiser, but not sure where to start?** We've got you covered! -## Guidelines +**Welcome to the Komiser Contributing Guide** where you'll find everything you need to get started with contributing to Komiser. -We recommend discussing your plans on our Discord (join our community server) head to the `contributors` section before starting to code - especially for more ambitious contributions. This gives other contributors a chance to point you in the right direction, give feedback on your design, and maybe point out if someone else is working on the same thing. +We are thrilled to have you as part of our community and looking forward to your valuable contributions! -Any significant improvements should be documented as a github issue before anybody starts working on it. Please take a moment to check that an issue doesnโ€™t already exist documenting your bug report or improvement proposal. If it does, it never hurts to add a quick โ€œ+1โ€ or โ€œI have this problem tooโ€. This will help prioritize the most common problems and requests +## Before You Get Started! -Feel free to communicate through the `#feedback` and `#feature-request` Discord channels. If we identify you as a `contributor`, we will add you to a private `#contributors` channel, to expedite internal communication (Hope to see you there!). +Before diving into contributions, here are a few things to keep in mind: -## Conventions +### For Major Feature Enhancements -Fork the repo and make changes on your fork in a feature branch based on the master branch: +**Planning to work on adding a major feature enhancement?** That's amazing and we always recommend discussing your plans with the team first. -- If itโ€™s a bugfix branch, name it fix/XXX-something where XXX is the number of the issue -- If itโ€™s a feature branch, create an enhancement issue to announce your intentions, and name it feature/XXX-something where XXX is the number of the issue. -- Submit unit tests for your changes. Go has a great test framework built in; use it! Take a look at existing tests for inspiration. Run the full test suite on your branch before submitting a pull request. -- Make sure you include relevant updates or additions to documentation when creating or modifying features. -- Write clean code. Universally formatted code promotes ease of writing, reading, and maintenance. Always run go fmt before committing your changes. Most editors have plugins that do this automatically. -- While submitting Pull Request, Always remember to change the base branch from master to develop. This will keep your Pull Request away from conflicts. **Master brach always reflects the releases and major fixes, So that it can be used by the end users.** +This provides an opportunity for fellow contributors to - **guide you in the right direction**, **offer feedback on your design**, and **potentially identify if another contributor is already working on a similar task**. -## How to add a new cloud provider? +Here's how to reach out: -1. Create a `provider.go` under `providers/provider` folder with the following content: +- Join the **#contributors** channel on our [Discord Server](https://discord.tailwarden.com) to start the discussion. +- Share your idea(s) in the **#feature-request** channel and get feedback from the community. + +### For Bug Reports and Minor Fixes + +**Found a bug and wish to fix it?** Each and every contribution matters regardless of its size! + +Here are a few things to keep in mind in this case: + +- Before creating a new issue, please make sure to check for an already [existing issue](https://github.com/tailwarden/komiser/issues) for that bug. +- If an issue doesn't exist, [create a new issue](https://github.com/tailwarden/komiser/issues/new/choose) of **`type: Bug Report`** and make sure to provide as much detail as possible. +- Feel free to reach out to us on **#contributors** or **#feedback** channel on our Discord Server, if you need any assistance or have questions. + +## General Contribution Flow + +This section covers the **general contribution flow** when contributing to any area of **Komiser (Engine or the UI)** and some best practices to follow along the way. + +Following these steps will ensure that your contributions are well-received, reviewed, and integrated effectively into Komiser's codebase. + +### Fork and Pull Request Flow + +1. Head over to the [Komiser GitHub repo](https://github.com/tailwarden/komiser) and "fork it" into your own GitHub account. +2. Clone your fork to your local machine, using the following command: + ```bash + git clone git@github.com:USERNAME/FORKED-PROJECT.git + ``` +3. Create a new branch based-off **`develop`** branch: + ```bash + git checkout develop + git checkout -b fix/XXX-something develop + ``` + Make sure to follow the following branch naming conventions: + - For feature/enchancements, use: **`feature/xxxx-name_of_feature`** + - For bug fixes, use: **`fix/xxxx-name_of_bug`**

+ + > Here, **`xxxx`** is the issue number associate with the bug/feature! + + For example: + ```bash + git checkout -b feature/1022-kubecost-integration develop + ``` +4. Implement the changes or additions you intend to contribute. Whether it's **bug fixes**, **new features**, or **enhancements**, this is where you put your coding skills to use. +5. Once your changes are ready, you may then commit and push the changes from your working branch: + ```bash + git commit -m "nice_commit_description" + git push origin feature/1022-kubecost-integration + ``` +6. While submitting Pull Request, **make sure to change the base branch from**: [master](https://github.com/tailwarden/komiser/tree/master) to [develop](v). This will ensure to keep your Pull Request away from any conflicts! + +> ### Keeping your Fork Up-to-Date +> +> If you plan on doing anything more than just a tiny quick fix, youโ€™ll want to **make sure you keep your fork up to date** by tracking the original [โ€œupstreamโ€ repo](https://github.com/tailwarden/komiser) that you forked. +> +> Follow the steps given below to do so: +> +> 1. Add the 'upstream' repo to list of remotes: +> +> ```bash +> git remote add upstream https://github.com/tailwarden/komiser.git +> ``` +> +> 2. Fetch upstream repoโ€™s branches and latest commits: +> +> ```bash +> git fetch upstream +> ``` +> +> 3. Checkout to the **`develop`** branch and merge the upstream: +> +> ```bash +> git checkout develop +> git merge upstream/develop +> ``` +> +> **Now, your local 'develop' branch is up-to-date with everything modified upstream!** + +## Contributing to Komiser Engine + +The core Komiser Engine is written in Go (Golang) and leverages Go Modules. Following are the pre-requisistes needed to run Komiser on your local machine: +1. Latest Go version (currently its **`1.19`**) must be installed if you want to build and/or make changes to the existing code. The binary **`go1.19`** should be available in your path.

+ > If you wish to keep multiple versions of Go in your system and don't want to disturb the existing ones, refer [the guide](https://go.dev/doc/manage-install). +2. Make sure that the **`GOPATH`** environment variable is configured appropriately. + +### Komiser Installation + +**Step 1: Installing Komiser CLI** + +You may follow the instructions given in the [documentation](https://docs.komiser.io/getting-started/installation) to install the **Komiser CLI**, according to your operating system. + +**Step 2: Connect to a Cloud Account** + +In order to deploy a **self-hosted (local) instance** of Komiser, the next step would be to connect your Komiser CLI to a cloud account of YOUR CHOICE. You may refer the documentation of the [supported cloud providers](https://docs.komiser.io/configuration/cloud-providers/aws) and follow the instructions using any one (Let's say AWS). + +**Step 3: Accessing the Komiser UI** + +Once the local Komiser instance is running, you can access the dashboard UI on **`http://localhost:3000`** + +![komiser-dashboard](https://hackmd.io/_uploads/Syo0bMtgT.png) + +### Ways to Contribute in Komiser Engine + + +Komiser is an open source cloud-agnostic resource manager which helps you break down your cloud resources cost at the resource level. + +As the tool is **cloud-agnostic**, we are always looking to add more number of **cloud providers** to the [existing list](https://docs.komiser.io/configuration/cloud-providers/aws) and a variety of different services/resources. + +Therefore, there are mainly three ways you can contribute to the Komiser Engine: + +#### 1. Adding a new Cloud Provider + +Here are the general steps to integrate a new cloud provider in Komiser: + +**Step 1:** +Create a new **`provider_name.go`** file under **`providers/provider_name`** directory. + +**Step 2:** +Add the following boilerplate code, which defines the structure of any new provider to be added: ```go -package aws +package PROVIDER_NAME import ( "context" @@ -54,15 +167,72 @@ func FetchProviderData(ctx context.Context, client ProviderClient, db *bun.DB) { } ``` -2. Add provider SDK client in `providers/providers.go` -3. Include provider configuration in TOML format under `config.toml` +Then, the main task is writing the code to fetch all the resources/services using the provider's Go client SDK. You may refer any [existing examples](https://github.com/tailwarden/komiser/tree/develop/providers) to understand better. + +**Step 3:** +Add the information about the appropriate provider's SDK client in [**`providers/provider.go`**](https://github.com/tailwarden/komiser/blob/develop/providers/providers.go) file: + +```go + +type ProviderClient struct { + AWSClient *aws.Config + DigitalOceanClient *godo.Client + OciClient common.ConfigurationProvider + CivoClient *civogo.Client + K8sClient *K8sClient + LinodeClient *linodego.Client + TencentClient *tccvm.Client + AzureClient *AzureClient + ScalewayClient *scw.Client + MongoDBAtlasClient *mongodbatlas.Client + GCPClient *GCPClient + Name string +} + +type AzureClient struct { + Credentials *azidentity.ClientSecretCredential + SubscriptionId string +} + +``` + +**Step 4:** +Add the provider configuration in TOML format in your **`config.toml`** file, which will be used by Komiser to configure your account with the CLI. + +An example configuration entry for configuring a Google Cloud account in the **`config.toml`** file would look like this: +``` +[[gcp]] +name="production" +source="ENVIRONMENT_VARIABLES" +# path=./path/to/credentials/file specify if 'CREDENTIALS_FILE' is used as source +profile="production" +``` + +**Step 5:** +Build a new Komiser binary with the latest code changes using: + +``` +go build +``` + +**Step 6:** +Start a new Komiser development server using this new binary: + +``` +./komiser start +``` + +**If everything goes well, you'll see a new cloud provider added in the Komiser Dashboard!** -## How to add a new cloud service/resource? +#### 2. Adding a new Cloud Service/Resource -The process to follow for adding a new cloud service is: +Here are the general steps to add a new service/resource for a cloud provider in Komiser: -1. Create a new folder under the `providers/providername/service` path called `servicename.go` -2. Inside the new file, add the following: +**Step 1:** +Create a new file **`servicename.go`** under the path **`providers/provider_name/servicename`** + +**Step 2:** +Add the following boilerplate code, which defines the structure of any new service/resource to be added for a cloud provider: ```go package service @@ -86,42 +256,156 @@ func MyServiceResources(ctx context.Context, client ProviderClient) ([]Resource, } ``` -3. Call the function from `providers/providername/providername.go` by adding `MyServiceResources()` to `listOfSupportedServices()` function. +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. + +``` +func listOfSupportedServices() []providers.FetchDataFunction { + return []providers.FetchDataFunction{ + ec2.Instances, + ec2.ElasticIps, + lambda.Functions, + ec2.Acls, + ec2.Subnets, + ec2.SecurityGroups, + ec2.AutoScalingGroups, + ec2.InternetGateways, + iam.Roles, + iam.InstanceProfiles, + iam.OIDCProviders, + iam.SamlProviders, + iam.Groups, +. +. +. +``` + +**Step 4:** +Repeat steps **`4,5,6`** accordingly and you'll see a new resource/service added to Komiser, in the dashboard! -## General tips +Additionally, [here](https://youtu.be/Vn5uc2elcVg?feature=shared) is a video tutorial of the entire process for your reference. -A few important things to note when adding functions that call the cloud provider API: +#### 3. Enhance existing Cloud service/resource -- If possible, always use an API call that allows you to fetch many resources at once -- Take pagination into account. Ensure you fetch all the resources. -- Make sure the resource has a tags JSON column (if possible). Sometimes this requires additional SDK calls. -- Code is required to be formatted using gofmt, this covers most code style requirements. It is also highly recommended to use goimports to automatically order imports. -- Please try to keep lines length under 80 characters, the exact number of characters is not strict but it generally helps with readability. +**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). -## How can I contribute to Komiser dashboard? +## Contributing to Komiser Dashboard UI -* Clone the project -* Install Go dependencies: +Komiser Dashboard is built on **Typescript** and **Next.js**. The entire frontend stack used is as follows: +- **Next.js** +- **Typescript** +- **Tailwind** +- **Storybook** +- **Jest** +- **React Testing Library** +Following are the pre-requisites needed to setup a Dev environment of Komiser dashboard: +- In nearly all cases, while contributing to Komiser UI, you will need to build and run the Komiser Server as well, using the CLI. Make sure to follow the steps mentioned in the **"Komiser Installation"** section above. +- Make sure to have all the **latest versions** of the frontend stack listed above. + +### Setup a local Developement Server + +Here are the steps to setup and access the Komiser dashboard: + +**Step 0:** + +Install the necessary Go dependencies using the following command: ``` go mod download ``` -* Switch to dashboard folder and install npm dependencies: +**Step 1:** +From the root folder, start the Komiser backend server using the following command: +``` +go run *.go start --config /path/to/config.toml +``` + +> As soon as you run this, you'll be able to access the dashboard at `http://localhost:3000`. +> +> An important point to note here is, this dashboard only reflects the changes from the **`master`** branch. +> +> For our purpose, we certainly need changes to be reflected from our development branch! +> Follow the steps given below to do so ๐Ÿ‘‡ +> + +**Step 2:** +Head over to the **`dashboard`** directory: ``` cd dashboard -npm install ``` -* Deploy to a local server: +**Step 3:** +Create a new environment variable in the **`.env`** file: +``` +EXT_PUBLIC_API_URL=http://localhost:3000 +``` +**Step 4:** +Install the necessary **`npm`** dependencies and start the dev server using the following command: ``` +npm install npm run dev ``` -* Once you implemented your frontend changes, build the artifact, build it as golang assets: +You'll be able to access the dashboard at **`http://localhost:3002/`** +![](https://hackmd.io/_uploads/ryvOPmFla.png) + +To understand the installation process in a bit more detail, you may refer the [video walkthrough](https://youtu.be/uwxj11-eRt8?feature=shared). + +### Understanding the UI components + +The Komiser UI components are being handled and organised using [Storybook](https://storybook.js.org/). +Refer the [components section](https://github.com/tailwarden/komiser/tree/develop/dashboard#components) of the dashboard README to understand the component conventions used. + +### Testing Your Changes + +The Komiser dashboard uses **Jest** and **React Testing Library** for unit tests. Refer the [testing section](https://github.com/tailwarden/komiser/tree/develop/dashboard#testing) of the dashboard README to understand how you can write simple unit tests, to validate your changes. + +### Building a Go Artifact + +Once you have implemented the necessary frontend changes, make sure to build a new Go artifact using the following command: ``` go-bindata-assetfs -o template.go dist/ dist/assets/images/ ``` + +## Contributing Best Practices + +Here are some best practices to follow during the development process to make your changes more structured and making it easier for us to review: + +1. **Write Comprehensive Unit Tests:** + + - When making code changes, be sure to include well-structured unit tests. + - Utilize [Go's built-in testing framework](https://pkg.go.dev/testing) for this purpose. + - Take inspiration from existing tests in the project. + - Before submitting your pull request, run the full test suite on your development branch to ensure your changes are thoroughly validated. + +2. **Keep Documentation Updated:** + + - Ensure relevant documentation is updated or added, according to new features being added or modified. + +3. **Prioritize Clean Code:** + + - Make sure to use **`go fmt`** to ensure uniform code formatting before committing your changes. + - Using IDEs and code editors like **VSCode** makes this easy, as they offer plugins that automate the formatting process. + +4. **Mindful Code Comments:** + + - Use comments to explain complex logic, algorithms, or any non-obvious parts of your code. + - Well-placed comments will make your code more accessible to others and will ultimately help in a smoother review process of your changes. + + +## Ending Note + +We hope this guide proves to be helpful and makes contributing to Komiser an exciting and fun process for you all. + +At the end, we wanna give you a **HUGE THANK YOU** for taking out your time in contributing and making Komiser better and more accessible to the community! + +Feel free to reach out to us on our [Discord Server](https://discord.tailwarden.com) if you need any assistance or have any questions. + +## License + +Komiser is an open-source software released under the [Elastic License 2.0 (ELv2)](https://github.com/tailwarden/komiser/blob/develop/LICENSE). \ No newline at end of file From 5be13150849acd622dfee778ed13c20cdb11a3d1 Mon Sep 17 00:00:00 2001 From: Vedank Pande Date: Thu, 5 Oct 2023 00:13:47 +0530 Subject: [PATCH 003/113] feat: add lambda eventsourcemapping collector --- providers/aws/aws.go | 1 + providers/aws/lambda/eventsourcemappings.go | 56 +++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 providers/aws/lambda/eventsourcemappings.go diff --git a/providers/aws/aws.go b/providers/aws/aws.go index 701d741f3..ecf1e862e 100644 --- a/providers/aws/aws.go +++ b/providers/aws/aws.go @@ -39,6 +39,7 @@ func listOfSupportedServices() []providers.FetchDataFunction { ec2.Instances, ec2.ElasticIps, lambda.Functions, + lambda.EventSourceMappings, ec2.Acls, ec2.Subnets, ec2.SecurityGroups, diff --git a/providers/aws/lambda/eventsourcemappings.go b/providers/aws/lambda/eventsourcemappings.go new file mode 100644 index 000000000..7ad61d108 --- /dev/null +++ b/providers/aws/lambda/eventsourcemappings.go @@ -0,0 +1,56 @@ +package lambda + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/service/lambda" + log "github.com/sirupsen/logrus" + "github.com/tailwarden/komiser/models" + "github.com/tailwarden/komiser/providers" +) + +func EventSourceMappings(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) { + var config lambda.ListEventSourceMappingsInput + resources := make([]models.Resource, 0) + lambdaClient := lambda.NewFromConfig(*client.AWSClient) + + result, err := lambdaClient.ListEventSourceMappings(context.Background(), &config) + if err != nil { + log.Errorf("ERROR: Failed to fetch EventSourceMappings: %v", err) + return resources, err + } + + for _, mapping := range result.EventSourceMappings { + + lambdaNameSplit := strings.Split(*mapping.FunctionArn, ":") + lambdaName := lambdaNameSplit[len(lambdaNameSplit)-1] + + resources = append(resources, models.Resource{ + Provider: "AWS", + Account: client.Name, + Service: "EventSourceMapping", + ResourceId: *mapping.UUID, + Region: client.AWSClient.Region, + Name: *mapping.UUID, + Cost: 0.0, + Metadata: map[string]string{ + "lambda": *mapping.EventSourceArn, + "source": *mapping.EventSourceArn, + }, + FetchedAt: time.Now(), + Link: fmt.Sprintf("https://%s.console.aws.amazon.com/lambda/home?region=%s#/functions/%s", client.AWSClient.Region, client.AWSClient.Region, lambdaName), + }) + } + + log.WithFields(log.Fields{ + "provider": "AWS", + "account": client.Name, + "region": client.AWSClient.Region, + "service": "EventSourceMapping", + "resources": len(resources), + }).Info("Fetched resources") + return resources, nil +} \ No newline at end of file From bfd699bad8d405b74a1054ec870f94dd64e733b4 Mon Sep 17 00:00:00 2001 From: Vedank Pande Date: Thu, 5 Oct 2023 01:02:36 +0530 Subject: [PATCH 004/113] fix typo in lambda metadata value --- providers/aws/lambda/eventsourcemappings.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/aws/lambda/eventsourcemappings.go b/providers/aws/lambda/eventsourcemappings.go index 7ad61d108..65f7d96d8 100644 --- a/providers/aws/lambda/eventsourcemappings.go +++ b/providers/aws/lambda/eventsourcemappings.go @@ -37,7 +37,7 @@ func EventSourceMappings(ctx context.Context, client providers.ProviderClient) ( Name: *mapping.UUID, Cost: 0.0, Metadata: map[string]string{ - "lambda": *mapping.EventSourceArn, + "lambda": *mapping.FunctionArn, "source": *mapping.EventSourceArn, }, FetchedAt: time.Now(), From 3116702edc38808212cd9e524aba1cdcea04ebd9 Mon Sep 17 00:00:00 2001 From: Vedank Pande Date: Thu, 5 Oct 2023 01:12:58 +0530 Subject: [PATCH 005/113] imports formatting --- providers/aws/lambda/eventsourcemappings.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/providers/aws/lambda/eventsourcemappings.go b/providers/aws/lambda/eventsourcemappings.go index 65f7d96d8..20a8f3b1f 100644 --- a/providers/aws/lambda/eventsourcemappings.go +++ b/providers/aws/lambda/eventsourcemappings.go @@ -5,9 +5,10 @@ import ( "fmt" "strings" "time" - - "github.com/aws/aws-sdk-go-v2/service/lambda" + log "github.com/sirupsen/logrus" + + "github.com/aws/aws-sdk-go-v2/service/lambda" "github.com/tailwarden/komiser/models" "github.com/tailwarden/komiser/providers" ) From 6069f9126e8a8dfa7d143452d46b26c4a92373bc Mon Sep 17 00:00:00 2001 From: Ahmed Samir Date: Wed, 4 Oct 2023 23:47:03 +0300 Subject: [PATCH 006/113] feat: added k8s nodes support Signed-off-by: Ahmed Samir --- providers/k8s/core/nodes.go | 77 +++++++++++++++++++++++++++++++++++++ providers/k8s/k8s.go | 1 + 2 files changed, 78 insertions(+) create mode 100644 providers/k8s/core/nodes.go diff --git a/providers/k8s/core/nodes.go b/providers/k8s/core/nodes.go new file mode 100644 index 000000000..f872e295c --- /dev/null +++ b/providers/k8s/core/nodes.go @@ -0,0 +1,77 @@ +package core + +import ( + "context" + "time" + + log "github.com/sirupsen/logrus" + + "github.com/tailwarden/komiser/models" + "github.com/tailwarden/komiser/providers" + oc "github.com/tailwarden/komiser/providers/k8s/opencost" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func Nodes(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) { + resources := make([]models.Resource, 0) + + var config metav1.ListOptions + + opencostEnabled := true + nodesCost, err := oc.GetOpencostInfo(client.K8sClient.OpencostBaseUrl, "node") + if err != nil { + log.Errorf("ERROR: Couldn't get nodes info from OpenCost: %v", err) + log.Warn("Opencost disabled") + opencostEnabled = false + } + + for { + res, err := client.K8sClient.Client.CoreV1().Nodes().List(ctx, config) + if err != nil { + return nil, err + } + + for _, node := range res.Items { + tags := make([]models.Tag, 0) + + for key, value := range node.Labels { + tags = append(tags, models.Tag{ + Key: key, + Value: value, + }) + } + + cost := 0.0 + if opencostEnabled { + cost = nodesCost[node.Name].TotalCost + } + + resources = append(resources, models.Resource{ + Provider: "Kubernetes", + Account: client.Name, + Service: "Node", + ResourceId: string(node.UID), + Name: node.Name, + Region: node.Namespace, + Cost: cost, + CreatedAt: node.CreationTimestamp.Time, + FetchedAt: time.Now(), + Tags: tags, + }) + } + + if res.GetContinue() == "" { + break + } + + config.Continue = res.GetContinue() + } + + log.WithFields(log.Fields{ + "provider": "Kubernetes", + "account": client.Name, + "service": "Node", + "resources": len(resources), + }).Info("Fetched resources") + return resources, nil +} diff --git a/providers/k8s/k8s.go b/providers/k8s/k8s.go index b9cd5abd1..55638274c 100644 --- a/providers/k8s/k8s.go +++ b/providers/k8s/k8s.go @@ -21,6 +21,7 @@ func listOfSupportedServices() []providers.FetchDataFunction { core.PersistentVolumeClaims, core.ServiceAccounts, core.Deployments, + core.Nodes, } } From 77fe1ec7378cf39e1ec19e2bea6e8359c27512f8 Mon Sep 17 00:00:00 2001 From: Ahmed Samir Date: Wed, 4 Oct 2023 23:48:33 +0300 Subject: [PATCH 007/113] chore: Add Nodes to list and remove Deployments dup Signed-off-by: Ahmed Samir --- providers/k8s/k8s.go | 1 - 1 file changed, 1 deletion(-) diff --git a/providers/k8s/k8s.go b/providers/k8s/k8s.go index 55638274c..6d675361d 100644 --- a/providers/k8s/k8s.go +++ b/providers/k8s/k8s.go @@ -20,7 +20,6 @@ func listOfSupportedServices() []providers.FetchDataFunction { core.PersistentVolumes, core.PersistentVolumeClaims, core.ServiceAccounts, - core.Deployments, core.Nodes, } } From cb7b2eb1aa1a0e6d06ac2f226a0cc3109eee2da0 Mon Sep 17 00:00:00 2001 From: Azanul Haque <42029519+Azanul@users.noreply.github.com> Date: Thu, 5 Oct 2023 05:38:33 +0530 Subject: [PATCH 008/113] Update README.md --- README.md | 107 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 9a80538cd..817f42387 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,26 @@

Amp Logo

-Komiser is an open-source cloud-agnostic resource manager designed to analyze and manage cloud cost, usage, security, and governance all in one place. It integrates seamlessly with multiple cloud providers, including AWS, Azure, Civo, Digital Ocean, OCI, Linode, Tencent, Scaleway and [more](#supported-cloud-providers). +Komiser is an open-source cloud-agnostic resource manager designed to analyze and manage cloud cost, usage, security, and governance all in one place. It integrates seamlessly with multiple cloud providers, including AWS, Azure, Civo, Digital Ocean, OCI, Linode, Tencent, Scaleway and [more](#supported-cloud-providers). Interested? read more about Komiser on our [website](https://komiser.io?utm_source=github&utm_medium=social). + +

+ + +Discord + + + +GitHub commit activity + + + +Docker pulls + + +

- Discord | Tailwarden Cloud | - Website

Guide | How to Komiser | Community Events

@@ -23,11 +37,6 @@ Komiser is an open-source cloud-agnostic resource manager designed to analyze an

-[![Build and Test komiser](https://github.com/tailwarden/komiser/actions/workflows/build_test.yml/badge.svg)](https://github.com/tailwarden/komiser/actions/workflows/build_test.yml) -[![Price](https://img.shields.io/badge/price-FREE-0098f7.svg)](https://github.com/tailwarden/komiser/blob/master/LICENSE) [![Docker Stars](https://img.shields.io/docker/pulls/mlabouardy/komiser.svg)](https://hub.docker.com/r/mlabouardy/komiser) -[![ELv2 License](https://img.shields.io/badge/license-ELv2-green)](LICENSE) [![Docker Stars](https://img.shields.io/github/issues/tailwarden/komiser.svg)](https://github.com/tailwarden/komiser/issues) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.tailwarden.com/) - -

Komiser gif

@@ -89,52 +98,67 @@ Komiser was built with every Cloud Engineer, Developer, DevOps engineer and SRE ## Supported cloud providers +## Getting started +### Installation -# Getting started ๐Ÿ‘‡ - -## Download - -You can run Komiser locally, as a Docker container or by running it inside a Kubernetes cluster. -Below are the available downloads for the latest version of Komiser. Please [download](https://docs.komiser.io/docs/introduction/getting-started?utm_source=github&utm_medium=social) the appropriate package for your operating system and architecture. +#### Linux -## Installation on AWS +``` +wget https://cli.komiser.io/latest/komiser_Linux_x86_64 -O komiser +``` -### Connect Komiser CLI to your AWS account. -* Connect a [local deployment](https://docs.komiser.io/docs/cloud-providers/aws/#local-komiser-cli) of Komiser CLI to you AWS account +#### Windows -### Deploy Komiser to single account access EKS cluster (Helm chart) -* If you want to connect a single AWS account follow the documentation [here](https://docs.komiser.io/docs/cloud-providers/aws/#eks-installation-single-account). +``` +wget https://cli.komiser.io/latest/komiser_Windows_x86_64.zip +tar -xf komiser_Windows_x86_64.zip +``` -Watch the installation [video here](https://www.youtube.com/watch?v=4veDmJpui44&t) +#### Mac OS X -### Deploy Komiser to a multi account access EKS cluster (Helm chart) -* If you would like to connect various AWS accounts to a Komiser deployment in a Management EKS cluster, follow the steps [here](https://docs.komiser.io/docs/cloud-providers/aws/#multiple-account-eks-helm-chart-installation). +ARM architecture (M1 & M2 Chip) +``` +wget https://cli.komiser.io/latest/komiser_Darwin_arm64 -O komiser +``` -## Installation on Azure +AMD architecture (Intel Chip) +``` +wget https://cli.komiser.io/latest/komiser_Darwin_x86_64 -O komiser +``` -Connect a local deployment of Komiser CLI to you [**Azure**](https://docs.komiser.io/configuration/cloud-providers/azure?utm_source=github&utm_medium=social) account. +#### Homebrew +``` +brew update +brew tap tailwarden/komiser +brew install komiser -## Installation on GCP +# Make sure you are running the newest version of Komiser: +brew update +brew reinstall komiser +``` -Connect a local deployment of Komiser CLI to you [**GCP**](https://docs.komiser.io/docs/cloud-providers/google-cloud-platform?utm_source=github&utm_medium=social) account. +### Configuration -## Installation on Civo +| Providers | Documentation | +| ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/k8s?utm_source=github&utm_medium=organic&utm_campaign=readme) | [Kubernetes documentation link](https://docs.komiser.io/configuration/cloud-providers/k8s?utm_source=github&utm_medium=organic&utm_campaign=readme) | +| [![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/aws?utm_source=github&utm_medium=organic&utm_campaign=readme) | [AWS documentation link ](https://docs.komiser.io/configuration/cloud-providers/aws?utm_source=github&utm_medium=organic&utm_campaign=readme) | +| [![Azure](https://img.shields.io/badge/azure-%230078D4.svg?style=for-the-badge&logo=microsoftazure&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/azure?utm_source=github&utm_medium=organic&utm_campaign=readme) | [Azure documentation link](https://docs.komiser.io/configuration/cloud-providers/azure?utm_source=github&utm_medium=organic&utm_campaign=readme) | +| [![Civo](https://img.shields.io/badge/Civo-%23239DFF.svg?style=for-the-badge&logo=civo&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/civo?utm_source=github&utm_medium=organic&utm_campaign=readme) | [Civo documentation link](https://docs.komiser.io/configuration/cloud-providers/civo?utm_source=github&utm_medium=organic&utm_campaign=readme) | +| [![Digital Ocean](https://img.shields.io/badge/DigitalOcean-%230080FF.svg?style=for-the-badge&logo=digitalocean&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/digital-ocean?utm_source=github&utm_medium=organic&utm_campaign=readme) | [Digital Ocean documentation link](https://docs.komiser.io/configuration/cloud-providers/digital-ocean?utm_source=github&utm_medium=organic&utm_campaign=readme) | +| [![Akamai (Linode)](https://img.shields.io/badge/Akamai-%230096D6.svg?style=for-the-badge&logo=akamai&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=organic&utm_campaign=readme) | [Akamai (Linode) documentation link](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=organic&utm_campaign=readme) | -Connect a local deployment of Komiser CLI to your [**Civo**](https://docs.komiser.io/docs/cloud-providers/civo?utm_source=github&utm_medium=social) account. -Watch the installation [video here](https://www.youtube.com/watch?v=NBbEpoW-kVs) -## Installation on OCI -Connect a local deployment of Komiser CLI to your [**OCI**](https://docs.komiser.io/docs/cloud-providers/oci?utm_source=github&utm_medium=social) account. -## Installation on Digital Ocean +## Installation on GCP -Connect a local deployment of Komiser CLI to you [**Digital Ocean**](https://docs.komiser.io/docs/cloud-providers/digital-ocean?utm_source=github&utm_medium=social) account. +Connect a local deployment of Komiser CLI to you [**GCP**](https://docs.komiser.io/docs/cloud-providers/google-cloud-platform?utm_source=github&utm_medium=social) account. -## Installation on Linode +## Installation on OCI -Connect a local deployment of Komiser CLI to you [**Linode**](https://docs.komiser.io/docs/cloud-providers/linode?utm_source=github&utm_medium=social) account. +Connect a local deployment of Komiser CLI to your [**OCI**](https://docs.komiser.io/docs/cloud-providers/oci?utm_source=github&utm_medium=social) account. ## Installation on Tencent Cloud @@ -145,19 +169,10 @@ Connect a local deployment of Komiser CLI to you [**Tencent**](https://docs.komi Connect a local deployment of Komiser CLI to you [**Scaleway**](https://docs.komiser.io/docs/cloud-providers/scaleway?utm_source=github&utm_medium=social) account. -## Documentation ๐Ÿ“– - -Head over to the official `Komiser` documentation at [docs.komiser.io](https://docs.komiser.io?utm_source=github&utm_medium=social). The source repository for the documentation website is [tailwarden/docs.komiser.io](https://github.com/tailwarden/docs.komiser.io). +## Resources ๐Ÿ“– -We know that writing docs isn't usually at the top of too many peoples "What I like to do for fun" list, but if you feel so inclined, by all means, consider [contributing](https://docs.komiser.io/docs/contributing/contribute?utm_source=github&utm_medium=social) to our documentation repository, we will be very grateful. It's built using [Docusaurus](https://docusaurus.io/). +* [Documentation](https://docs.komiser.io?utm_source=github&utm_medium=social) -### Jump right in: -* [Documentation overview](https://docs.komiser.io/docs/intro?utm_source=github&utm_medium=social) -* [Installation](https://docs.komiser.io/docs/introduction/getting-started?utm_source=github&utm_medium=social) -* [FAQs](https://docs.komiser.io/docs/faqs?utm_source=github&utm_medium=social) -* Video series: ๐Ÿ“น - * [How to: Komiser](https://www.youtube.com/watch?v=9pCimmIT-HQ&list=PLFIcIMmOFDZeMzcvOi7bPd4I6xUNq3A5R/alerts) - * [Installation videos](https://www.youtube.com/watch?v=urxi9z2IUf4&list=PLFIcIMmOFDZfaO_WmUF_qnF8akCII7Uk_) ## Bugs and feature requests ๐Ÿž From 056216de98f7e5da643321cf1d34868ef9ed03e5 Mon Sep 17 00:00:00 2001 From: Vedank Pande Date: Thu, 5 Oct 2023 12:17:07 +0530 Subject: [PATCH 009/113] fix: handle paginated output for eventsourcemappings --- providers/aws/lambda/eventsourcemappings.go | 67 ++++++++++++--------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/providers/aws/lambda/eventsourcemappings.go b/providers/aws/lambda/eventsourcemappings.go index 20a8f3b1f..d740868cf 100644 --- a/providers/aws/lambda/eventsourcemappings.go +++ b/providers/aws/lambda/eventsourcemappings.go @@ -5,45 +5,56 @@ import ( "fmt" "strings" "time" - + log "github.com/sirupsen/logrus" - + + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/lambda" "github.com/tailwarden/komiser/models" "github.com/tailwarden/komiser/providers" ) func EventSourceMappings(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) { - var config lambda.ListEventSourceMappingsInput resources := make([]models.Resource, 0) lambdaClient := lambda.NewFromConfig(*client.AWSClient) - - result, err := lambdaClient.ListEventSourceMappings(context.Background(), &config) - if err != nil { - log.Errorf("ERROR: Failed to fetch EventSourceMappings: %v", err) - return resources, err + mappingsPerPage := aws.Int32(50) + params := &lambda.ListEventSourceMappingsInput{ + MaxItems: mappingsPerPage, } - for _, mapping := range result.EventSourceMappings { - - lambdaNameSplit := strings.Split(*mapping.FunctionArn, ":") - lambdaName := lambdaNameSplit[len(lambdaNameSplit)-1] - - resources = append(resources, models.Resource{ - Provider: "AWS", - Account: client.Name, - Service: "EventSourceMapping", - ResourceId: *mapping.UUID, - Region: client.AWSClient.Region, - Name: *mapping.UUID, - Cost: 0.0, - Metadata: map[string]string{ - "lambda": *mapping.FunctionArn, - "source": *mapping.EventSourceArn, - }, - FetchedAt: time.Now(), - Link: fmt.Sprintf("https://%s.console.aws.amazon.com/lambda/home?region=%s#/functions/%s", client.AWSClient.Region, client.AWSClient.Region, lambdaName), - }) + paginator := lambda.NewListEventSourceMappingsPaginator(lambdaClient, params, func(options *lambda.ListEventSourceMappingsPaginatorOptions) { + options.Limit = *mappingsPerPage + }) + + for paginator.HasMorePages() { + + output, err := paginator.NextPage(context.Background()) + if err != nil { + log.Errorf("ERROR: Error occurred while retrieving EventSourceMappings page: %v", err) + return resources, err + } + + for _, mapping := range output.EventSourceMappings { + + lambdaNameSplit := strings.Split(*mapping.FunctionArn, ":") + lambdaName := lambdaNameSplit[len(lambdaNameSplit)-1] + + resources = append(resources, models.Resource{ + Provider: "AWS", + Account: client.Name, + Service: "EventSourceMapping", + ResourceId: *mapping.UUID, + Region: client.AWSClient.Region, + Name: *mapping.UUID, + Cost: 0.0, + Metadata: map[string]string{ + "lambda": *mapping.FunctionArn, + "source": *mapping.EventSourceArn, + }, + FetchedAt: time.Now(), + Link: fmt.Sprintf("https://%s.console.aws.amazon.com/lambda/home?region=%s#/functions/%s", client.AWSClient.Region, client.AWSClient.Region, lambdaName), + }) + } } log.WithFields(log.Fields{ From ba64d5034b5d78360c552bcfcf3897d9ad97c40b Mon Sep 17 00:00:00 2001 From: Vedank Pande Date: Thu, 5 Oct 2023 13:18:01 +0530 Subject: [PATCH 010/113] update context passed to paginator --- providers/aws/lambda/eventsourcemappings.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/aws/lambda/eventsourcemappings.go b/providers/aws/lambda/eventsourcemappings.go index d740868cf..fa961ed80 100644 --- a/providers/aws/lambda/eventsourcemappings.go +++ b/providers/aws/lambda/eventsourcemappings.go @@ -28,7 +28,7 @@ func EventSourceMappings(ctx context.Context, client providers.ProviderClient) ( for paginator.HasMorePages() { - output, err := paginator.NextPage(context.Background()) + output, err := paginator.NextPage(ctx) if err != nil { log.Errorf("ERROR: Error occurred while retrieving EventSourceMappings page: %v", err) return resources, err From f21b94ee8a4c869c72cb9b902a67b6060eeb13f0 Mon Sep 17 00:00:00 2001 From: Kunal Verma Date: Thu, 5 Oct 2023 14:09:07 +0530 Subject: [PATCH 011/113] update contributing.md --- CONTRIBUTING.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a1bec2b2c..9fd542fef 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,11 +10,11 @@ We are thrilled to have you as part of our community and looking forward to your ## Before You Get Started! -Before diving into contributions, here are a few things to keep in mind: +Before getting started with your first contribution, here are a few things to keep in mind: ### For Major Feature Enhancements -**Planning to work on adding a major feature enhancement?** That's amazing and we always recommend discussing your plans with the team first. +**Planning to work on adding a major feature enhancement?** That's amazing we always encourage ambitious contributions. **Keep in mind though that we always recommend discussing your plans with the team first.** This provides an opportunity for fellow contributors to - **guide you in the right direction**, **offer feedback on your design**, and **potentially identify if another contributor is already working on a similar task**. @@ -55,7 +55,7 @@ Following these steps will ensure that your contributions are well-received, rev - For feature/enchancements, use: **`feature/xxxx-name_of_feature`** - For bug fixes, use: **`fix/xxxx-name_of_bug`**

- > Here, **`xxxx`** is the issue number associate with the bug/feature! + > Here, **`xxxx`** is the issue number associated with the bug/feature! For example: ```bash @@ -67,7 +67,7 @@ Following these steps will ensure that your contributions are well-received, rev git commit -m "nice_commit_description" git push origin feature/1022-kubecost-integration ``` -6. While submitting Pull Request, **make sure to change the base branch from**: [master](https://github.com/tailwarden/komiser/tree/master) to [develop](v). This will ensure to keep your Pull Request away from any conflicts! +6. While submitting Pull Request, **make sure to change the base branch from**: [master](https://github.com/tailwarden/komiser/tree/master) to [develop](v). Making sure to avoid any possible merge conflicts > ### Keeping your Fork Up-to-Date > @@ -107,11 +107,11 @@ The core Komiser Engine is written in Go (Golang) and leverages Go Modules. Foll **Step 1: Installing Komiser CLI** -You may follow the instructions given in the [documentation](https://docs.komiser.io/getting-started/installation) to install the **Komiser CLI**, according to your operating system. +Follow the instructions given in the [documentation](https://docs.komiser.io/getting-started/installation) to install the **Komiser CLI**, according to your operating system. **Step 2: Connect to a Cloud Account** -In order to deploy a **self-hosted (local) instance** of Komiser, the next step would be to connect your Komiser CLI to a cloud account of YOUR CHOICE. You may refer the documentation of the [supported cloud providers](https://docs.komiser.io/configuration/cloud-providers/aws) and follow the instructions using any one (Let's say AWS). +In order to deploy a **self-hosted (local) instance** of Komiser, the next step would be to connect your Komiser CLI to a cloud account of your choice. You may refer the documentation of the [supported cloud providers](https://docs.komiser.io/configuration/cloud-providers/aws) and follow the instructions using any one (Let's say AWS). **Step 3: Accessing the Komiser UI** @@ -124,11 +124,11 @@ Once the local Komiser instance is running, you can access the dashboard UI on * Komiser is an open source cloud-agnostic resource manager which helps you break down your cloud resources cost at the resource level. -As the tool is **cloud-agnostic**, we are always looking to add more number of **cloud providers** to the [existing list](https://docs.komiser.io/configuration/cloud-providers/aws) and a variety of different services/resources. +Due to the nature of Komiser, a cloud-agnostic cloud management tool, our work is never really done! There are always more providers and cloud services that can be added, updated and cost calculated. Therefore, there are mainly three ways you can contribute to the Komiser Engine: -#### 1. Adding a new Cloud Provider +### 1. Adding a new Cloud Provider Here are the general steps to integrate a new cloud provider in Komiser: @@ -209,7 +209,7 @@ profile="production" ``` **Step 5:** -Build a new Komiser binary with the latest code changes using: +Build a new Komiser binary with the latest code changes by running: ``` go build @@ -224,7 +224,7 @@ Start a new Komiser development server using this new binary: **If everything goes well, you'll see a new cloud provider added in the Komiser Dashboard!** -#### 2. Adding a new Cloud Service/Resource +### 2. Adding a new Cloud Service/Resource Here are the general steps to add a new service/resource for a cloud provider in Komiser: From ea3f6c4b9d4575f1af13567e4871c2f477c4f6c6 Mon Sep 17 00:00:00 2001 From: Azanul Haque <42029519+Azanul@users.noreply.github.com> Date: Thu, 5 Oct 2023 15:44:24 +0530 Subject: [PATCH 012/113] Update README.md --- README.md | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 817f42387..4eda74622 100644 --- a/README.md +++ b/README.md @@ -140,38 +140,25 @@ brew reinstall komiser ### Configuration -| Providers | Documentation | -| ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/k8s?utm_source=github&utm_medium=organic&utm_campaign=readme) | [Kubernetes documentation link](https://docs.komiser.io/configuration/cloud-providers/k8s?utm_source=github&utm_medium=organic&utm_campaign=readme) | -| [![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/aws?utm_source=github&utm_medium=organic&utm_campaign=readme) | [AWS documentation link ](https://docs.komiser.io/configuration/cloud-providers/aws?utm_source=github&utm_medium=organic&utm_campaign=readme) | -| [![Azure](https://img.shields.io/badge/azure-%230078D4.svg?style=for-the-badge&logo=microsoftazure&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/azure?utm_source=github&utm_medium=organic&utm_campaign=readme) | [Azure documentation link](https://docs.komiser.io/configuration/cloud-providers/azure?utm_source=github&utm_medium=organic&utm_campaign=readme) | -| [![Civo](https://img.shields.io/badge/Civo-%23239DFF.svg?style=for-the-badge&logo=civo&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/civo?utm_source=github&utm_medium=organic&utm_campaign=readme) | [Civo documentation link](https://docs.komiser.io/configuration/cloud-providers/civo?utm_source=github&utm_medium=organic&utm_campaign=readme) | -| [![Digital Ocean](https://img.shields.io/badge/DigitalOcean-%230080FF.svg?style=for-the-badge&logo=digitalocean&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/digital-ocean?utm_source=github&utm_medium=organic&utm_campaign=readme) | [Digital Ocean documentation link](https://docs.komiser.io/configuration/cloud-providers/digital-ocean?utm_source=github&utm_medium=organic&utm_campaign=readme) | -| [![Akamai (Linode)](https://img.shields.io/badge/Akamai-%230096D6.svg?style=for-the-badge&logo=akamai&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=organic&utm_campaign=readme) | [Akamai (Linode) documentation link](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=organic&utm_campaign=readme) | - - - - -## Installation on GCP - -Connect a local deployment of Komiser CLI to you [**GCP**](https://docs.komiser.io/docs/cloud-providers/google-cloud-platform?utm_source=github&utm_medium=social) account. - -## Installation on OCI - -Connect a local deployment of Komiser CLI to your [**OCI**](https://docs.komiser.io/docs/cloud-providers/oci?utm_source=github&utm_medium=social) account. - -## Installation on Tencent Cloud - -Connect a local deployment of Komiser CLI to you [**Tencent**](https://docs.komiser.io/docs/cloud-providers/tencent?utm_source=github&utm_medium=social) account. - -## Installation on Scaleway - -Connect a local deployment of Komiser CLI to you [**Scaleway**](https://docs.komiser.io/docs/cloud-providers/scaleway?utm_source=github&utm_medium=social) account. +| Providers | Documentation | Video | +| ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | +| [![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/k8s?utm_source=github&utm_medium=social&utm_campaign=readme) | [Kubernetes documentation link](https://docs.komiser.io/configuration/cloud-providers/k8s?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/aws?utm_source=github&utm_medium=social&utm_campaign=readme) | [AWS documentation link ](https://docs.komiser.io/configuration/cloud-providers/aws?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![Azure](https://img.shields.io/badge/azure-%230078D4.svg?style=for-the-badge&logo=microsoftazure&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/azure?utm_source=github&utm_medium=social&utm_campaign=readme) | [Azure documentation link](https://docs.komiser.io/configuration/cloud-providers/azure?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![Civo](https://img.shields.io/badge/Civo-%23239DFF.svg?style=for-the-badge&logo=civo&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/civo?utm_source=github&utm_medium=social&utm_campaign=readme) | [Civo documentation link](https://docs.komiser.io/configuration/cloud-providers/civo?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![Digital Ocean](https://img.shields.io/badge/DigitalOcean-%230080FF.svg?style=for-the-badge&logo=digitalocean&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/digital-ocean?utm_source=github&utm_medium=social&utm_campaign=readme) | [Digital Ocean documentation link](https://docs.komiser.io/configuration/cloud-providers/digital-ocean?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![Akamai (Linode)](https://img.shields.io/badge/Akamai-%230096D6.svg?style=for-the-badge&logo=akamai&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=social&utm_campaign=readme) | [Akamai (Linode) documentation link](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![GCP](https://img.shields.io/badge/GCP-%234285F4.svg?style=for-the-badge&logo=googlecloud&logoColor=white)](https://docs.komiser.io/docs/cloud-providers/google-cloud-platform?utm_source=github&utm_medium=social&utm_campaign=readme) | [GCP documentation link](https://docs.komiser.io/docs/cloud-providers/google-cloud-platform?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![OCI](https://img.shields.io/badge/OCI-%23F80000.svg?style=for-the-badge&logo=oracle&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/oracle?utm_source=github&utm_medium=social&utm_campaign=readme) | [Oracle Cloud Infrastructure documentation link](https://docs.komiser.io/configuration/cloud-providers/oracle?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![Tencent Cloud](https://img.shields.io/badge/Tencent-%230096D6.svg?style=for-the-badge&logo=tencentcloud&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=social&utm_campaign=readme) | [Tencent Cloud documentation link](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![Scaleway](https://img.shields.io/badge/Scaleway-%234F0599.svg?style=for-the-badge&logo=scaleway&logoColor=white)](https://docs.komiser.io/docs/cloud-providers/scaleway?utm_source=github&utm_medium=social&utm_campaign=readme) | [Scaleway documentation link](https://docs.komiser.io/docs/cloud-providers/scaleway?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![MongoDB Atlas](https://img.shields.io/badge/MongoDB-%2347A248.svg?style=for-the-badge&logo=mongodb&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=social&utm_campaign=readme) | [MongoDB Atlas documentation link](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=social&utm_campaign=readme) | | ## Resources ๐Ÿ“– * [Documentation](https://docs.komiser.io?utm_source=github&utm_medium=social) +* [Videos](https://www.youtube.com/@tailwarden/videos) ## Bugs and feature requests ๐Ÿž From 5e453377c55f5f4075ae057e00341a25da629cd1 Mon Sep 17 00:00:00 2001 From: Azanul Haque <42029519+Azanul@users.noreply.github.com> Date: Thu, 5 Oct 2023 18:27:42 +0530 Subject: [PATCH 013/113] Update README.md --- README.md | 38 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 4eda74622..f461e43e2 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Komiser is an open-source cloud-agnostic resource manager designed to analyze and manage cloud cost, usage, security, and governance all in one place. It integrates seamlessly with multiple cloud providers, including AWS, Azure, Civo, Digital Ocean, OCI, Linode, Tencent, Scaleway and [more](#supported-cloud-providers). Interested? read more about Komiser on our [website](https://komiser.io?utm_source=github&utm_medium=social). +If youโ€™re using Komiser or if you like the project, please โญ this repository to show your support! ๐Ÿคฉ +

@@ -22,6 +24,8 @@ Komiser is an open-source cloud-agnostic resource manager designed to analyze an

+

Komiser gif

+

Tailwarden Cloud | Guide | @@ -31,28 +35,6 @@ Komiser is an open-source cloud-agnostic resource manager designed to analyze an Roadmap

-

- - - -

- - -

Komiser gif

- -The fastest and most reliable way to get started with Komiser is signing up for free to [Tailwarden Cloud](https://cloud.tailwarden.com?utm_source=github&utm_medium=social) - -[![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/fold_left.svg?style=social&label=Follow%20%40Komiser)](https://twitter.com/komiserdotio) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Optimize%20Cost%20and%20Security%20on%20AWS&url=https://github.com/tailwarden/komiser&via=mlabouardy&hashtags=komiser,aws,gcp,cloud,serverless,devops) - -## Komiser CLI, try it out! ๐Ÿš€ ---- -The easiest way to get started with Komiser is to install the [Komiser CLI](https://docs.komiser.io/docs/introduction/getting-started?utm_source=github&utm_medium=social) by running the `Homebrew` commands below. Don't have Homebrew? Install it [here](https://docs.brew.sh/Installation). - -``` -brew tap tailwarden/komiser -brew install komiser -``` - **Table of Contents** @@ -96,12 +78,14 @@ Komiser is an open source project created to **analyse** and **manage cloud cost ## Who is using it? Komiser was built with every Cloud Engineer, Developer, DevOps engineer and SRE in mind. We understand that tackling cost savings, security improvements and resource usage analyse efforts can be hard, sometimes just knowing where to start, can be the most challenging part at times. Komiser is here to help those cloud practitioners see their cloud resources and accounts much more clearly. Only with clear insight can timely and efficient actions take place. -## Supported cloud providers - ## Getting started ### Installation +#### Tailwarden Cloud (Free & Recommended) + +Head over to [Tailwarden](https://cloud.tailwarden.com?utm_source=github&utm_medium=social&utm_campaign=readme). + #### Linux ``` @@ -142,10 +126,10 @@ brew reinstall komiser | Providers | Documentation | Video | | ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | -| [![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/k8s?utm_source=github&utm_medium=social&utm_campaign=readme) | [Kubernetes documentation link](https://docs.komiser.io/configuration/cloud-providers/k8s?utm_source=github&utm_medium=social&utm_campaign=readme) | | -| [![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/aws?utm_source=github&utm_medium=social&utm_campaign=readme) | [AWS documentation link ](https://docs.komiser.io/configuration/cloud-providers/aws?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/k8s?utm_source=github&utm_medium=social&utm_campaign=readme) | [Kubernetes documentation link](https://docs.komiser.io/configuration/cloud-providers/k8s?utm_source=github&utm_medium=social&utm_campaign=readme) | [Komiser + k8s](https://youtu.be/hSiVFjD0u3I) | +| [![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/aws?utm_source=github&utm_medium=social&utm_campaign=readme) | [AWS documentation link ](https://docs.komiser.io/configuration/cloud-providers/aws?utm_source=github&utm_medium=social&utm_campaign=readme) | [Komiser + AWS EKS](https://youtu.be/4veDmJpui44) | | [![Azure](https://img.shields.io/badge/azure-%230078D4.svg?style=for-the-badge&logo=microsoftazure&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/azure?utm_source=github&utm_medium=social&utm_campaign=readme) | [Azure documentation link](https://docs.komiser.io/configuration/cloud-providers/azure?utm_source=github&utm_medium=social&utm_campaign=readme) | | -| [![Civo](https://img.shields.io/badge/Civo-%23239DFF.svg?style=for-the-badge&logo=civo&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/civo?utm_source=github&utm_medium=social&utm_campaign=readme) | [Civo documentation link](https://docs.komiser.io/configuration/cloud-providers/civo?utm_source=github&utm_medium=social&utm_campaign=readme) | | +| [![Civo](https://img.shields.io/badge/Civo-%23239DFF.svg?style=for-the-badge&logo=civo&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/civo?utm_source=github&utm_medium=social&utm_campaign=readme) | [Civo documentation link](https://docs.komiser.io/configuration/cloud-providers/civo?utm_source=github&utm_medium=social&utm_campaign=readme) | [Komiser + Civo](https://youtu.be/NBbEpoW-kVs) | | [![Digital Ocean](https://img.shields.io/badge/DigitalOcean-%230080FF.svg?style=for-the-badge&logo=digitalocean&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/digital-ocean?utm_source=github&utm_medium=social&utm_campaign=readme) | [Digital Ocean documentation link](https://docs.komiser.io/configuration/cloud-providers/digital-ocean?utm_source=github&utm_medium=social&utm_campaign=readme) | | | [![Akamai (Linode)](https://img.shields.io/badge/Akamai-%230096D6.svg?style=for-the-badge&logo=akamai&logoColor=white)](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=social&utm_campaign=readme) | [Akamai (Linode) documentation link](https://docs.komiser.io/configuration/cloud-providers/linode?utm_source=github&utm_medium=social&utm_campaign=readme) | | | [![GCP](https://img.shields.io/badge/GCP-%234285F4.svg?style=for-the-badge&logo=googlecloud&logoColor=white)](https://docs.komiser.io/docs/cloud-providers/google-cloud-platform?utm_source=github&utm_medium=social&utm_campaign=readme) | [GCP documentation link](https://docs.komiser.io/docs/cloud-providers/google-cloud-platform?utm_source=github&utm_medium=social&utm_campaign=readme) | | From 7e865c5778759ede1bf8081b7f667e6bb49d7997 Mon Sep 17 00:00:00 2001 From: Azanul Haque <42029519+Azanul@users.noreply.github.com> Date: Thu, 5 Oct 2023 18:29:26 +0530 Subject: [PATCH 014/113] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f461e43e2..0a93190b5 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,7 @@ brew reinstall komiser * [Documentation](https://docs.komiser.io?utm_source=github&utm_medium=social) * [Videos](https://www.youtube.com/@tailwarden/videos) +* [Blog](https://www.tailwarden.com/blog) ## Bugs and feature requests ๐Ÿž From dd4e991a20c530ef1903ca73c3e22ec91e5d8913 Mon Sep 17 00:00:00 2001 From: jbleduigou Date: Thu, 5 Oct 2023 20:47:36 +0200 Subject: [PATCH 015/113] Adding Kinesis Data Streams to the list of supported resources --- docs/configuration/cloud-providers/aws.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/configuration/cloud-providers/aws.mdx b/docs/configuration/cloud-providers/aws.mdx index 5813009a1..b39c80be3 100644 --- a/docs/configuration/cloud-providers/aws.mdx +++ b/docs/configuration/cloud-providers/aws.mdx @@ -32,6 +32,7 @@ sidebar_label: Amazon Web Services - IAM policies - IAM roles - IAM SAML providers + - Kinesis Data Streams - KMS keys - Lambda functions - RDS clusters @@ -502,4 +503,4 @@ metadata: volumes: - name: test-volume configMap: - name: aws-configmap \ No newline at end of file + name: aws-configmap From f366fe86e73463185a2f2d1fffd7169c85bc1b6f Mon Sep 17 00:00:00 2001 From: shubhindia Date: Fri, 6 Oct 2023 12:15:47 +0530 Subject: [PATCH 016/113] add cost for k8s services Signed-off-by: shubhindia --- providers/k8s/core/services.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/providers/k8s/core/services.go b/providers/k8s/core/services.go index f4b1a7ce7..ae0127490 100644 --- a/providers/k8s/core/services.go +++ b/providers/k8s/core/services.go @@ -8,6 +8,7 @@ import ( . "github.com/tailwarden/komiser/models" "github.com/tailwarden/komiser/providers" + oc "github.com/tailwarden/komiser/providers/k8s/opencost" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -16,6 +17,14 @@ func Services(ctx context.Context, client providers.ProviderClient) ([]Resource, var config metav1.ListOptions + opencostEnabled := true + serviceCost, err := oc.GetOpencostInfo(client.K8sClient.OpencostBaseUrl, "service") + if err != nil { + log.Errorf("ERROR: Couldn't get service info from OpenCost: %v", err) + log.Warn("Opencost disabled") + opencostEnabled = false + } + for { res, err := client.K8sClient.Client.CoreV1().Services("").List(ctx, config) if err != nil { @@ -32,6 +41,11 @@ func Services(ctx context.Context, client providers.ProviderClient) ([]Resource, }) } + cost := 0.0 + if opencostEnabled { + cost = serviceCost[service.Name].TotalCost + } + resources = append(resources, Resource{ Provider: "Kubernetes", Account: client.Name, @@ -39,7 +53,7 @@ func Services(ctx context.Context, client providers.ProviderClient) ([]Resource, ResourceId: string(service.UID), Name: service.Name, Region: service.Namespace, - Cost: 0, + Cost: cost, CreatedAt: service.CreationTimestamp.Time, FetchedAt: time.Now(), Tags: tags, From 59bb1acf2bfcd7420870c2557eeeae3ab49942e3 Mon Sep 17 00:00:00 2001 From: Ahmed Samir Date: Fri, 6 Oct 2023 16:08:30 +0300 Subject: [PATCH 017/113] feat: added k8s namespaces support Signed-off-by: Ahmed Samir --- providers/k8s/core/namespaces.go | 77 ++++++++++++++++++++++++++++++++ providers/k8s/k8s.go | 1 + 2 files changed, 78 insertions(+) create mode 100644 providers/k8s/core/namespaces.go diff --git a/providers/k8s/core/namespaces.go b/providers/k8s/core/namespaces.go new file mode 100644 index 000000000..829c8b2f6 --- /dev/null +++ b/providers/k8s/core/namespaces.go @@ -0,0 +1,77 @@ +package core + +import ( + "context" + "time" + + log "github.com/sirupsen/logrus" + + "github.com/tailwarden/komiser/models" + "github.com/tailwarden/komiser/providers" + oc "github.com/tailwarden/komiser/providers/k8s/opencost" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func Namespaces(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) { + resources := make([]models.Resource, 0) + + var config metav1.ListOptions + + opencostEnabled := true + namespacesCost, err := oc.GetOpencostInfo(client.K8sClient.OpencostBaseUrl, "namespace") + if err != nil { + log.Errorf("ERROR: Couldn't get namespaces info from OpenCost: %v", err) + log.Warn("Opencost disabled") + opencostEnabled = false + } + + for { + res, err := client.K8sClient.Client.CoreV1().Namespaces().List(ctx, config) + if err != nil { + return nil, err + } + + for _, namespace := range res.Items { + tags := make([]models.Tag, 0) + + for key, value := range namespace.Labels { + tags = append(tags, models.Tag{ + Key: key, + Value: value, + }) + } + + cost := 0.0 + if opencostEnabled { + cost = namespacesCost[namespace.Name].TotalCost + } + + resources = append(resources, models.Resource{ + Provider: "Kubernetes", + Account: client.Name, + Service: "Namespace", + ResourceId: string(namespace.UID), + Name: namespace.Name, + Region: namespace.Namespace, + Cost: cost, + CreatedAt: namespace.CreationTimestamp.Time, + FetchedAt: time.Now(), + Tags: tags, + }) + } + + if res.GetContinue() == "" { + break + } + + config.Continue = res.GetContinue() + } + + log.WithFields(log.Fields{ + "provider": "Kubernetes", + "account": client.Name, + "service": "Namespace", + "resources": len(resources), + }).Info("Fetched resources") + return resources, nil +} diff --git a/providers/k8s/k8s.go b/providers/k8s/k8s.go index 6d675361d..3b52335b7 100644 --- a/providers/k8s/k8s.go +++ b/providers/k8s/k8s.go @@ -21,6 +21,7 @@ func listOfSupportedServices() []providers.FetchDataFunction { core.PersistentVolumeClaims, core.ServiceAccounts, core.Nodes, + core.Namespaces, } } From 60859155e603acc0960a9215ffef805d65696cc5 Mon Sep 17 00:00:00 2001 From: jbleduigou Date: Sat, 7 Oct 2023 14:18:27 +0200 Subject: [PATCH 018/113] doc: path is a string and should use double quotes --- CONTRIBUTING.md | 2 +- docs/configuration/cloud-providers/aws.mdx | 8 ++++---- docs/getting-started/quickstart.mdx | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9fd542fef..591bd8dca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -204,7 +204,7 @@ An example configuration entry for configuring a Google Cloud account in the **` [[gcp]] name="production" source="ENVIRONMENT_VARIABLES" -# path=./path/to/credentials/file specify if 'CREDENTIALS_FILE' is used as source +# path="./path/to/credentials/file" specify if 'CREDENTIALS_FILE' is used as source profile="production" ``` diff --git a/docs/configuration/cloud-providers/aws.mdx b/docs/configuration/cloud-providers/aws.mdx index 5813009a1..7db1d54c6 100644 --- a/docs/configuration/cloud-providers/aws.mdx +++ b/docs/configuration/cloud-providers/aws.mdx @@ -97,19 +97,19 @@ Add to config.toml file [[aws]] name="sandbox" source="CREDENTIALS_FILE" -path=./path/to/credentials/file +path="./path/to/credentials/file" profile="default" [[aws]] name="staging" source="CREDENTIALS_FILE" -path=./path/to/credentials/file +path="./path/to/credentials/file" profile="staging-account" [[gcp]] name="production" source="ENVIRONMENT_VARIABLES" -# path=./path/to/credentials/file specify if CREDENTIALS_FILE is used +# path="./path/to/credentials/file" specify if CREDENTIALS_FILE is used profile="production" [postgres] @@ -136,7 +136,7 @@ profile="production" [[aws]] name="sandbox" source="CREDENTIALS_FILE" -path=./path/to/credentials/file +path="./path/to/credentials/file" profile="default" ``` ### Credentials file diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx index 2104a2a34..913d177a9 100644 --- a/docs/getting-started/quickstart.mdx +++ b/docs/getting-started/quickstart.mdx @@ -56,19 +56,19 @@ The reason for this external data persistence is to improve the filtering, sorti [[aws]] name="sandbox" source="CREDENTIALS_FILE" -path=./path/to/credentials/file +path="./path/to/credentials/file" profile="default" [[aws]] name="staging" source="CREDENTIALS_FILE" -path=./path/to/credentials/file +path="./path/to/credentials/file" profile="staging-account" [[gcp]] name="production" source="ENVIRONMENT_VARIABLES" -# path=./path/to/credentials/file specify if CREDENTIALS_FILE is used +# path="./path/to/credentials/file" specify if CREDENTIALS_FILE is used profile="production" [postgres] From ebc8de48812137ea1c1dc6ae736067d8b84380dd Mon Sep 17 00:00:00 2001 From: shubhindia Date: Mon, 9 Oct 2023 09:37:29 +0530 Subject: [PATCH 019/113] chore: drop deprecated ioutil Signed-off-by: shubhindia --- internal/api/v1/template.go | 3 +-- internal/config/load.go | 5 ++--- providers/gcp/compute/instances_test.go | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/internal/api/v1/template.go b/internal/api/v1/template.go index 61e843bfd..41543e448 100644 --- a/internal/api/v1/template.go +++ b/internal/api/v1/template.go @@ -127,7 +127,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strings" @@ -3013,7 +3012,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/internal/config/load.go b/internal/config/load.go index e9e22f39c..e39ced17d 100644 --- a/internal/config/load.go +++ b/internal/config/load.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "io/ioutil" "log" "net/http" "os" @@ -46,7 +45,7 @@ func loadConfigFromFile(path string) (*Config, error) { return nil, fmt.Errorf("no such file %s", filename) } - yamlFile, err := ioutil.ReadFile(filename) + yamlFile, err := os.ReadFile(filename) if err != nil { return nil, err } @@ -436,7 +435,7 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics, db *bun. accounts = append(accounts, cloudAccount) - data, err := ioutil.ReadFile(account.ServiceAccountKeyPath) + data, err := os.ReadFile(account.ServiceAccountKeyPath) if err != nil { log.Fatal(err) } diff --git a/providers/gcp/compute/instances_test.go b/providers/gcp/compute/instances_test.go index 4cb406dd7..c00192dc4 100644 --- a/providers/gcp/compute/instances_test.go +++ b/providers/gcp/compute/instances_test.go @@ -3,7 +3,7 @@ package compute import ( "context" "fmt" - "io/ioutil" + "os" "testing" "github.com/tailwarden/komiser/providers" @@ -13,7 +13,7 @@ import ( func TestInstances(t *testing.T) { t.Skip("Only for local development because it is using a Google Cloud connection") // Replace the empty string with a SA or credentials file location - data, err := ioutil.ReadFile("") + data, err := os.ReadFile("") if err != nil { t.Fatal(err) } From 92f9e092934a97c320d3800c3be0cfd69c3992f8 Mon Sep 17 00:00:00 2001 From: Azanul Date: Mon, 9 Oct 2023 09:52:42 +0530 Subject: [PATCH 020/113] doctoc Signed-off-by: Azanul --- README.md | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 0a93190b5..459a4a253 100644 --- a/README.md +++ b/README.md @@ -39,29 +39,24 @@ If youโ€™re using Komiser or if you like the project, please โญ this repository **Table of Contents** -- [What is Komiser? ๐Ÿคท](#what-is-komiser-) +- [](#) + - [What is Komiser? ๐Ÿคท](#what-is-komiser-) - [Who is using it?](#who-is-using-it) -- [Getting started ๐Ÿ‘‡](#getting-started-) - - [Download](#download) - - [Installation on AWS](#installation-on-aws) - - [Connect Komiser CLI to your AWS account.](#connect-komiser-cli-to-your-aws-account) - - [Deploy Komiser to single account access EKS cluster (Helm chart)](#deploy-komiser-to-single-account-access-eks-cluster-helm-chart) - - [Deploy Komiser to a multi account access EKS cluster (Helm chart)](#deploy-komiser-to-a-multi-account-access-eks-cluster-helm-chart) - - [Installation on Azure](#installation-on-azure) - - [Installation on GCP](#installation-on-gcp) - - [Installation on Civo](#installation-on-civo) - - [Installation on OCI](#installation-on-oci) - - [Installation on Digital Ocean](#installation-on-digital-ocean) - - [Installation on Linode](#installation-on-linode) - - [Installation on Tencent Cloud](#installation-on-tencent-cloud) - - [Installation on Scaleway](#installation-on-scaleway) -- [Documentation ๐Ÿ“–](#documentation-) - - [Jump right in:](#jump-right-in) -- [Bugs and feature requests ๐Ÿž](#bugs-and-feature-requests-) -- [Roadmap and Contributing ๐Ÿ›ฃ๏ธ](#roadmap-and-contributing-๏ธ) -- [Users ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘](#users-) -- [Versioning ๐Ÿงฎ](#versioning-) -- [Contributors](#contributors) + - [Getting started](#getting-started) + - [Installation](#installation) + - [Tailwarden Cloud (Free & Recommended)](#tailwarden-cloud-free--recommended) + - [Linux](#linux) + - [Windows](#windows) + - [Mac OS X](#mac-os-x) + - [Homebrew](#homebrew) + - [Configuration](#configuration) + - [Resources ๐Ÿ“–](#resources-) + - [Bugs and feature requests ๐Ÿž](#bugs-and-feature-requests-) + - [Roadmap and Contributing ๐Ÿ›ฃ๏ธ](#roadmap-and-contributing-) + - [Users ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘](#users-) + - [Contributors](#contributors) + - [Versioning ๐Ÿงฎ](#versioning-) + - [License](#license) From 284cdd6d45e5ca9c054ede43576f75bec1611e57 Mon Sep 17 00:00:00 2001 From: bishal7679 Date: Mon, 9 Oct 2023 12:04:44 +0530 Subject: [PATCH 021/113] cost of s3 showing 0 is fixed Signed-off-by: bishal7679 --- providers/aws/s3/buckets.go | 95 ++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 13 deletions(-) diff --git a/providers/aws/s3/buckets.go b/providers/aws/s3/buckets.go index 3401e99fd..eb42a941c 100644 --- a/providers/aws/s3/buckets.go +++ b/providers/aws/s3/buckets.go @@ -9,15 +9,18 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + cloudwatchTypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/aws/aws-sdk-go-v2/service/pricing" + "github.com/aws/aws-sdk-go-v2/service/pricing/types" "github.com/aws/aws-sdk-go-v2/service/s3" . "github.com/tailwarden/komiser/models" . "github.com/tailwarden/komiser/providers" + awsUtils "github.com/tailwarden/komiser/providers/aws/utils" "github.com/tailwarden/komiser/utils" ) func ConvertBytesToTerabytes(bytes int64) float64 { - return float64(bytes) / 1000000000000 + return float64(bytes) / 1099511627776 } func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { @@ -25,53 +28,119 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { var config s3.ListBucketsInput s3Client := s3.NewFromConfig(*client.AWSClient) cloudwatchClient := cloudwatch.NewFromConfig(*client.AWSClient) + pricingClient := pricing.NewFromConfig(*client.AWSClient) + + pricingOutput, err := pricingClient.GetProducts(ctx, &pricing.GetProductsInput{ + ServiceCode: aws.String("AmazonS3"), + Filters: []types.Filter{ + { + Field: aws.String("regionCode"), // Filter by region + Value: aws.String(client.AWSClient.Region), + Type: types.FilterTypeTermMatch, + }, + { + Field: aws.String("productFamily"), + Value: aws.String("Storage"), + Type: types.FilterTypeTermMatch, + }, + }, + }) + if err != nil { + log.Errorf("ERROR: Couldn't fetch pricing info for AWS S3: %v", err) + return resources, err + } + + priceMap, err := awsUtils.GetPriceMap(pricingOutput) + if err != nil { + log.Errorf("ERROR: Failed to calculate cost per month: %v", err) + return resources, err + } + + // --------------------------------------------------------------- output, err := s3Client.ListBuckets(context.Background(), &config) if err != nil { return resources, err } for _, bucket := range output.Buckets { + // metrics for bucket size metricsBucketSizebytesOutput, err := cloudwatchClient.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{ StartTime: aws.Time(utils.BeginningOfMonth(time.Now())), EndTime: aws.Time(time.Now()), MetricName: aws.String("BucketSizeBytes"), Namespace: aws.String("AWS/S3"), - Dimensions: []types.Dimension{ - types.Dimension{ + Dimensions: []cloudwatchTypes.Dimension{ + { Name: aws.String("BucketName"), Value: bucket.Name, }, - types.Dimension{ + { Name: aws.String("StorageType"), Value: aws.String("StandardStorage"), }, }, - Unit: types.StandardUnitBytes, + Unit: cloudwatchTypes.StandardUnitBytes, Period: aws.Int32(3600), - Statistics: []types.Statistic{ - types.StatisticAverage, + Statistics: []cloudwatchTypes.Statistic{ + cloudwatchTypes.StatisticAverage, }, }) if err != nil { log.Warnf("Couldn't fetch invocations metric for %s", *bucket.Name) } - bucketSize := 0.0 if metricsBucketSizebytesOutput != nil && len(metricsBucketSizebytesOutput.Datapoints) > 0 { bucketSize = *metricsBucketSizebytesOutput.Datapoints[0].Average } sizeInTB := ConvertBytesToTerabytes(int64(bucketSize)) - monthlyCost := 0.0 + storageCostPerGB := 0.0 if sizeInTB <= 50 { - monthlyCost = (sizeInTB * 1000) * 0.023 + storageCostPerGB = (sizeInTB * 1024) * 0.023 } else if sizeInTB <= 450 { - monthlyCost = (sizeInTB * 1000) * 0.022 + storageCostPerGB = (sizeInTB * 1024) * 0.022 } else { - monthlyCost = (sizeInTB * 1000) * 0.021 + storageCostPerGB = (sizeInTB * 1024) * 0.021 + } + + // metrics for bucket usage + + metricsUsageOutput, err := cloudwatchClient.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{ + StartTime: aws.Time(utils.BeginningOfMonth(time.Now())), + EndTime: aws.Time(time.Now()), + MetricName: aws.String("AllRequests"), + Namespace: aws.String("AWS/S3"), + Dimensions: []cloudwatchTypes.Dimension{ + { + Name: aws.String("BucketName"), + Value: bucket.Name, + }, + { + Name: aws.String("StorageType"), + Value: aws.String("StandardStorage"), + }, + }, + Unit: cloudwatchTypes.StandardUnitCount, + Period: aws.Int32(3600), + Statistics: []cloudwatchTypes.Statistic{ + cloudwatchTypes.StatisticSum, + }, + }) + if err != nil { + log.Warnf("Couldn't fetch usage metric for %s", *bucket.Name) } + requestCount := 0.0 + if metricsUsageOutput != nil && len(metricsUsageOutput.Datapoints) > 0 { + requestCount = *metricsUsageOutput.Datapoints[0].Sum + } + // requestCost := (requestCount / 1000) * 0.0004 + + monthlyCost := 0.0 + + requestCharges := awsUtils.GetCost(priceMap["AWS-S3-Requests"], requestCount/1000) // charges per 1000 request + monthlyCost = storageCostPerGB + requestCharges tagsResp, err := s3Client.GetBucketTagging(context.Background(), &s3.GetBucketTaggingInput{ Bucket: bucket.Name, }) From ec41b03f0af2e07cd484377f49f861471bcac332 Mon Sep 17 00:00:00 2001 From: shubhindia Date: Mon, 9 Oct 2023 14:27:59 +0530 Subject: [PATCH 022/113] add linter Signed-off-by: shubhindia --- .github/workflows/lint.yml | 19 ++++++++ go.mod | 2 +- handlers/feedback_handler.go | 2 +- ...413100000_set_default_tags_in_resources.go | 2 +- ..._create_custom_webhook_fields_in_alerts.go | 6 +-- .../20230619100000_add_new_relation_field.go | 4 +- .../20231014100000_add_new_status_field.go | 2 +- providers/aws/ec2/elastic_ips.go | 24 +++++----- providers/aws/elasticache/clusters.go | 2 +- providers/gcp/kms/keys.go | 2 +- utils/database.go | 4 +- utils/gcpcomputepricing/disk.go | 2 +- utils/gcpcomputepricing/machine.go | 44 +++++-------------- utils/gcpcomputepricing/snapshot.go | 9 +--- 14 files changed, 59 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..e9e75a346 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,19 @@ +name: Lint +on: + push: + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup go + uses: actions/setup-go@v4 + with: + go-version-file: go.mod + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + args: --timeout 5m + skip-cache: true \ No newline at end of file diff --git a/go.mod b/go.mod index 1275883b9..30fa0426d 100644 --- a/go.mod +++ b/go.mod @@ -190,7 +190,7 @@ require ( golang.org/x/tools v0.6.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea + google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea // indirect google.golang.org/grpc v1.54.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/handlers/feedback_handler.go b/handlers/feedback_handler.go index 190de3c14..12ddd1f6a 100644 --- a/handlers/feedback_handler.go +++ b/handlers/feedback_handler.go @@ -52,7 +52,7 @@ func (handler *ApiHandler) NewFeedbackHandler(c *gin.Context) { ] }`, description, email) - payloadField.Write([]byte(payloadJSON)) + _, _ = payloadField.Write([]byte(payloadJSON)) imagePart, err := writer.CreateFormFile("files[0]", filepath.Base("temp-image")) if err != nil { diff --git a/migrations/20230413100000_set_default_tags_in_resources.go b/migrations/20230413100000_set_default_tags_in_resources.go index be74b782b..0946ec806 100644 --- a/migrations/20230413100000_set_default_tags_in_resources.go +++ b/migrations/20230413100000_set_default_tags_in_resources.go @@ -11,7 +11,7 @@ func init() { // Set default value for tags in resources table, because sometimes it was 'null' // The Resources model was updated to set the default value to []string{}, but all older instances // of Komiser didn't have that default value set, so we need to update the database - db.NewUpdate(). + _, _ = db.NewUpdate(). Table("resources"). Set("tags = ?", []string{}). Where("tags = 'null'"). diff --git a/migrations/20230602100000_create_custom_webhook_fields_in_alerts.go b/migrations/20230602100000_create_custom_webhook_fields_in_alerts.go index 63d39624e..f86798ab1 100644 --- a/migrations/20230602100000_create_custom_webhook_fields_in_alerts.go +++ b/migrations/20230602100000_create_custom_webhook_fields_in_alerts.go @@ -13,17 +13,17 @@ func init() { `) if err != nil { - db.ExecContext(ctx, ` + _, _ = db.ExecContext(ctx, ` ALTER TABLE alerts ADD COLUMN is_slack BOOLEAN DEFAULT 1; `) - db.ExecContext(ctx, ` + _, _ = db.ExecContext(ctx, ` ALTER TABLE alerts ADD COLUMN endpoint TEXT; `) - db.ExecContext(ctx, ` + _, _ = db.ExecContext(ctx, ` ALTER TABLE alerts ADD COLUMN secret TEXT; `) diff --git a/migrations/20230619100000_add_new_relation_field.go b/migrations/20230619100000_add_new_relation_field.go index 4b1e60688..ad7f9f4d7 100644 --- a/migrations/20230619100000_add_new_relation_field.go +++ b/migrations/20230619100000_add_new_relation_field.go @@ -11,9 +11,9 @@ func init() { Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error { // adding new column relation for migration if db.Dialect().Name() == dialect.SQLite { - db.ExecContext(ctx, "ALTER TABLE resources ADD COLUMN relations TEXT DEFAULT '[]';") + _, _ = db.ExecContext(ctx, "ALTER TABLE resources ADD COLUMN relations TEXT DEFAULT '[]';") } else { - db.ExecContext(ctx, "ALTER TABLE resources ADD COLUMN relations JSONB DEFAULT '[]'::jsonb;") + _, _ = db.ExecContext(ctx, "ALTER TABLE resources ADD COLUMN relations JSONB DEFAULT '[]'::jsonb;") } return nil }, func(ctx context.Context, db *bun.DB) error { diff --git a/migrations/20231014100000_add_new_status_field.go b/migrations/20231014100000_add_new_status_field.go index 087b4c638..fd0ee46f8 100644 --- a/migrations/20231014100000_add_new_status_field.go +++ b/migrations/20231014100000_add_new_status_field.go @@ -9,7 +9,7 @@ import ( func init() { Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error { // adding new column relation for migration - db.ExecContext(ctx, "ALTER TABLE accounts ADD COLUMN status TEXT;") + _, _ = db.ExecContext(ctx, "ALTER TABLE accounts ADD COLUMN status TEXT;") return nil }, func(ctx context.Context, db *bun.DB) error { // No rollback needed diff --git a/providers/aws/ec2/elastic_ips.go b/providers/aws/ec2/elastic_ips.go index 5149596b8..afe353115 100644 --- a/providers/aws/ec2/elastic_ips.go +++ b/providers/aws/ec2/elastic_ips.go @@ -26,7 +26,7 @@ func ElasticIps(ctx context.Context, client ProviderClient) ([]Resource, error) stsClient := sts.NewFromConfig(*client.AWSClient) stsOutput, err := stsClient.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{}) if err != nil { - return resources, err + return resources, err } accountId := stsOutput.Account @@ -45,9 +45,9 @@ func ElasticIps(ctx context.Context, client ProviderClient) ([]Resource, error) Value: *tag.Value, }) } - + cost := 0.0 - + resourceConfig, err := configClient.BatchGetResourceConfig(ctx, &configservice.BatchGetResourceConfigInput{ ResourceKeys: []types.ResourceKey{ { @@ -86,7 +86,6 @@ func ElasticIps(ctx context.Context, client ProviderClient) ([]Resource, error) } } - resourceArn := fmt.Sprintf("arn:aws:ec2:%s:%s:elastic-ip/%s", client.AWSClient.Region, *accountId, *elasticIps.AllocationId) relations := getEIPRelations(&elasticIps, fmt.Sprintf("arn:aws:ec2:%s:%s", client.AWSClient.Region, *accountId)) @@ -101,33 +100,34 @@ func ElasticIps(ctx context.Context, client ProviderClient) ([]Resource, error) FetchedAt: time.Now(), Tags: tags, Link: fmt.Sprintf("https:/%s.console.aws.amazon.com/ec2/home?region=%s#ElasticIpDetails:AllocationId=%s", client.AWSClient.Region, client.AWSClient.Region, *elasticIps.AllocationId), - Relations: relations, + Relations: relations, }) } - + log.WithFields(log.Fields{ "provider": "AWS", "account": client.Name, "region": client.AWSClient.Region, "service": "Elastic IP", "resources": len(resources), - }).Info("Fetched resources") + }).Info("Fetched resources") + + // nolint:staticcheck // SA4004 ignore this return resources, nil } } - func getEIPRelations(ip *etype.Address, resourceArn string) (rel []models.Link) { - + if ip.InstanceId != nil { id := fmt.Sprintf("%s:instance/%s", resourceArn, *ip.InstanceId) rel = append(rel, models.Link{ ResourceID: id, - Type: "EC2", - Relation: "USES", + Type: "EC2", + Relation: "USES", }) } - return + return } func hoursSince(t time.Time) float64 { diff --git a/providers/aws/elasticache/clusters.go b/providers/aws/elasticache/clusters.go index 02d947661..de2be2361 100644 --- a/providers/aws/elasticache/clusters.go +++ b/providers/aws/elasticache/clusters.go @@ -81,7 +81,7 @@ func Clusters(ctx context.Context, client ProviderClient) ([]Resource, error) { MaxResults: aws.Int32(1), }) if err != nil { - log.Warnf("Couldn't fetch pricing information for %s", cluster) + log.Warnf("Couldn't fetch pricing information for %s", *cluster.ARN) } hourlyCost := 0.0 diff --git a/providers/gcp/kms/keys.go b/providers/gcp/kms/keys.go index 662e2bfa2..4cd4d5276 100644 --- a/providers/gcp/kms/keys.go +++ b/providers/gcp/kms/keys.go @@ -7,12 +7,12 @@ import ( "time" kms "cloud.google.com/go/kms/apiv1" + kmspb "cloud.google.com/go/kms/apiv1/kmspb" "github.com/sirupsen/logrus" "github.com/tailwarden/komiser/models" "github.com/tailwarden/komiser/providers" "google.golang.org/api/iterator" "google.golang.org/api/option" - kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" ) func Keys(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) { diff --git a/utils/database.go b/utils/database.go index e5324e8c8..74239fc6c 100644 --- a/utils/database.go +++ b/utils/database.go @@ -89,7 +89,9 @@ func SetupSchema(db *bun.DB, c *models.Config, accounts []models.Account) error func doMigrations(db *bun.DB, ctx context.Context) error { migrator := migrate.NewMigrator(db, migrations.Migrations) - migrator.Init(ctx) + if err := migrator.Init(ctx); err != nil { + return err + } group, err := migrator.Migrate(ctx) if err != nil { diff --git a/utils/gcpcomputepricing/disk.go b/utils/gcpcomputepricing/disk.go index 589f6a403..9480bff03 100644 --- a/utils/gcpcomputepricing/disk.go +++ b/utils/gcpcomputepricing/disk.go @@ -108,7 +108,7 @@ func getDiskMonthly(p *Pricing, opts Opts, tg typeDiskGetter) (uint64, error) { capacityPricePerRegion = region.Prices[0].Nanos } } else { - return 0, errors.New(fmt.Sprintf("capacity price not found for %q region", opts.Region)) + return 0, fmt.Errorf("capacity price not found for %q region", opts.Region) } var sum uint64 = 0 diff --git a/utils/gcpcomputepricing/machine.go b/utils/gcpcomputepricing/machine.go index c6edd2461..e5d8b3936 100644 --- a/utils/gcpcomputepricing/machine.go +++ b/utils/gcpcomputepricing/machine.go @@ -136,9 +136,7 @@ func typeGetterE2(p *Pricing, opts Opts) (Subtype, Subtype, error) { core = p.Gcp.Compute.GCE.VmsCommit3Year.CoresPerCore.Commitmente2CPU3Yv1 memory = p.Gcp.Compute.GCE.VmsCommit3Year.MemoryPerGb.Commitmente2RAM3Yv1 default: - return Subtype{}, Subtype{}, errors.New( - fmt.Sprintf("commitment %q not supported", opts.Commitment), - ) + return Subtype{}, Subtype{}, fmt.Errorf("commitment %q not supported", opts.Commitment) } return core, memory, nil } @@ -161,9 +159,7 @@ func typeGetterC3(p *Pricing, opts Opts) (Subtype, Subtype, error) { core = p.Gcp.Compute.GCE.VmsCommit3Year.CoresPerCore.C3.Commitmentc3CPU3Yv1 memory = p.Gcp.Compute.GCE.VmsCommit3Year.MemoryPerGb.C3.Commitmentc3RAM3Yv1 default: - return Subtype{}, Subtype{}, errors.New( - fmt.Sprintf("commitment %q not supported", opts.Commitment), - ) + return Subtype{}, Subtype{}, fmt.Errorf("commitment %q not supported", opts.Commitment) } return core, memory, nil } @@ -185,9 +181,7 @@ func typeGetterN2(p *Pricing, opts Opts) (Subtype, Subtype, error) { core = p.Gcp.Compute.GCE.VmsCommit3Year.CoresPerCore.Commitmentn2CPU3Yv1 memory = p.Gcp.Compute.GCE.VmsCommit3Year.MemoryPerGb.Commitmentn2RAM3Yv1 default: - return Subtype{}, Subtype{}, errors.New( - fmt.Sprintf("commitment %q not supported", opts.Commitment), - ) + return Subtype{}, Subtype{}, fmt.Errorf("commitment %q not supported", opts.Commitment) } return core, memory, nil } @@ -209,9 +203,7 @@ func typeGetterN2D(p *Pricing, opts Opts) (Subtype, Subtype, error) { core = p.Gcp.Compute.GCE.VmsCommit3Year.CoresPerCore.Commitmentn2Dcpu3Yv1 memory = p.Gcp.Compute.GCE.VmsCommit3Year.MemoryPerGb.Commitmentn2Dram3Yv1 default: - return Subtype{}, Subtype{}, errors.New( - fmt.Sprintf("commitment %q not supported", opts.Commitment), - ) + return Subtype{}, Subtype{}, fmt.Errorf("commitment %q not supported", opts.Commitment) } return core, memory, nil } @@ -227,9 +219,7 @@ func typeGetterT2A(p *Pricing, opts Opts) (Subtype, Subtype, error) { core = p.Gcp.Compute.GCE.VmsPreemptible.CoresPerCore.T2A.Vmimagepreemptiblet2Astandardcore memory = p.Gcp.Compute.GCE.VmsPreemptible.MemoryPerGb.T2A.Vmimagepreemptiblet2Astandardram default: - return Subtype{}, Subtype{}, errors.New( - fmt.Sprintf("commitment %q not supported", opts.Commitment), - ) + return Subtype{}, Subtype{}, fmt.Errorf("commitment %q not supported", opts.Commitment) } return core, memory, nil } @@ -251,9 +241,7 @@ func typeGetterT2D(p *Pricing, opts Opts) (Subtype, Subtype, error) { core = p.Gcp.Compute.GCE.VmsCommit3Year.CoresPerCore.Commitmentt2Dcpu3Yv1 memory = p.Gcp.Compute.GCE.VmsCommit3Year.MemoryPerGb.Commitmentt2Dram3Yv1 default: - return Subtype{}, Subtype{}, errors.New( - fmt.Sprintf("commitment %q not supported", opts.Commitment), - ) + return Subtype{}, Subtype{}, fmt.Errorf("commitment %q not supported", opts.Commitment) } return core, memory, nil } @@ -269,9 +257,7 @@ func typeGetterN1(p *Pricing, opts Opts) (Subtype, Subtype, error) { core = p.Gcp.Compute.GCE.VmsPreemptible.CoresPerCore.Vmimagepreemptiblen1Standardcore memory = p.Gcp.Compute.GCE.VmsPreemptible.MemoryPerGb.Vmimagepreemptiblen1Standardram default: - return Subtype{}, Subtype{}, errors.New( - fmt.Sprintf("commitment %q not supported", opts.Commitment), - ) + return Subtype{}, Subtype{}, fmt.Errorf("commitment %q not supported", opts.Commitment) } return core, memory, nil } @@ -293,9 +279,7 @@ func typeGetterC2D(p *Pricing, opts Opts) (Subtype, Subtype, error) { core = p.Gcp.Compute.GCE.VmsCommit3Year.CoresPerCore.Commitmentc2Dcpu3Yv1 memory = p.Gcp.Compute.GCE.VmsCommit3Year.MemoryPerGb.Commitmentc2Dram3Yv1 default: - return Subtype{}, Subtype{}, errors.New( - fmt.Sprintf("commitment %q not supported", opts.Commitment), - ) + return Subtype{}, Subtype{}, fmt.Errorf("commitment %q not supported", opts.Commitment) } return core, memory, nil } @@ -317,9 +301,7 @@ func typeGetterM3(p *Pricing, opts Opts) (Subtype, Subtype, error) { core = p.Gcp.Compute.GCE.VmsCommit3Year.CoresPerCore.M3.Commitmentm3CPU3Yv1 memory = p.Gcp.Compute.GCE.VmsCommit3Year.MemoryPerGb.M3.Commitmentm3RAM3Yv1 default: - return Subtype{}, Subtype{}, errors.New( - fmt.Sprintf("commitment %q not supported", opts.Commitment), - ) + return Subtype{}, Subtype{}, fmt.Errorf("commitment %q not supported", opts.Commitment) } return core, memory, nil } @@ -336,9 +318,7 @@ func getHourly(p *Pricing, opts Opts, tg typeMachineGetter) (uint64, error) { corePricePerRegion = region.Prices[0].Nanos } } else { - return 0, errors.New( - fmt.Sprintf("core price not found for %q region", opts.Region), - ) + return 0, fmt.Errorf("core price not found for %q region", opts.Region) } var memoryPricePerRegion uint64 = 0 @@ -347,9 +327,7 @@ func getHourly(p *Pricing, opts Opts, tg typeMachineGetter) (uint64, error) { memoryPricePerRegion = region.Prices[0].Nanos } } else { - return 0, errors.New( - fmt.Sprintf("memory not found for %q region", opts.Region), - ) + return 0, fmt.Errorf("memory not found for %q region", opts.Region) } var sum uint64 = 0 diff --git a/utils/gcpcomputepricing/snapshot.go b/utils/gcpcomputepricing/snapshot.go index 7fe5a8897..c7cfb3b2f 100644 --- a/utils/gcpcomputepricing/snapshot.go +++ b/utils/gcpcomputepricing/snapshot.go @@ -2,7 +2,6 @@ package gcpcomputepricing import ( "context" - "errors" "fmt" "time" @@ -47,9 +46,8 @@ func CalculateSnapshotCost(ctx context.Context, client providers.ProviderClient, } func typeSnapshotGetter(p *Pricing, opts Opts) (Subtype, error) { - var capacity Subtype // TODO: switch by snapshot type - capacity = p.Gcp.Compute.PersistentDisk.Snapshots.Storageregionalstandardsnapshotearlydeletion + capacity := p.Gcp.Compute.PersistentDisk.Snapshots.Storageregionalstandardsnapshotearlydeletion return capacity, nil } @@ -66,10 +64,7 @@ func getSnapshotMonthly(p *Pricing, opts Opts, tg func(*Pricing, Opts) (Subtype, capacityPricePerRegion = region.Prices[0].Nanos } } else { - return 0, errors.New(fmt.Sprintf( - "capacity price not found for %q region", - opts.Region, - )) + return 0, fmt.Errorf("capacity price not found for %q region", opts.Region) } normalizedSize := uint64(storageBytes) / 1024 / 1024 / 1024 From 0f23943d861d88c817b93a2fe8aa0a22d0a96a4c Mon Sep 17 00:00:00 2001 From: shubhindia Date: Mon, 9 Oct 2023 14:34:50 +0530 Subject: [PATCH 023/113] remove lint timeout Signed-off-by: shubhindia --- .github/workflows/lint.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e9e75a346..99169ab24 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,7 +13,4 @@ jobs: with: go-version-file: go.mod - name: golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - args: --timeout 5m - skip-cache: true \ No newline at end of file + uses: golangci/golangci-lint-action@v3 \ No newline at end of file From 0aa93973eecdf18abc05db5f9723073d7e7c882a Mon Sep 17 00:00:00 2001 From: shubhindia Date: Mon, 9 Oct 2023 14:41:16 +0530 Subject: [PATCH 024/113] 30m timeout for lint Signed-off-by: shubhindia --- .github/workflows/lint.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 99169ab24..4579db448 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,4 +13,6 @@ jobs: with: go-version-file: go.mod - name: golangci-lint - uses: golangci/golangci-lint-action@v3 \ No newline at end of file + uses: golangci/golangci-lint-action@v3 + with: + args: --timeout=30m \ No newline at end of file From 5db0254d13168298dfe545da83eb0494b60206b0 Mon Sep 17 00:00:00 2001 From: Shubham Gopale Date: Mon, 9 Oct 2023 10:09:07 +0000 Subject: [PATCH 025/113] Update .github/workflows/lint.yml Co-authored-by: Azanul Haque <42029519+Azanul@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4579db448..e17db886f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,7 +4,7 @@ on: jobs: lint: - name: Lint + name: golangci-lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From 8d754f75f1d8768ea3862cf45dfb1a78cf3d074a Mon Sep 17 00:00:00 2001 From: shubhindia Date: Mon, 9 Oct 2023 15:42:06 +0530 Subject: [PATCH 026/113] add golangci-lint version and reduce the timeout to 10m Signed-off-by: shubhindia --- .github/workflows/lint.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e17db886f..dffddfc31 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,4 +15,5 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: - args: --timeout=30m \ No newline at end of file + args: --timeout=10m + version: v1.54.2 \ No newline at end of file From 08a93da5e084f293cb7c63babb410a38d42c0733 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 10:18:05 +0000 Subject: [PATCH 027/113] chore(deps): bump zod and next in /dashboard Removes [zod](https://github.com/colinhacks/zod). It's no longer used after updating ancestor dependency [next](https://github.com/vercel/next.js). These dependencies need to be updated together. Removes `zod` Updates `next` from 13.5.3 to 13.5.4 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v13.5.3...v13.5.4) --- updated-dependencies: - dependency-name: zod dependency-type: indirect - dependency-name: next dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- dashboard/package-lock.json | 125 +++++++++++++----------------------- dashboard/package.json | 2 +- 2 files changed, 47 insertions(+), 80 deletions(-) diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 0a0f5ec59..44c5dde14 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -17,7 +17,7 @@ "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-node-html-label": "^1.2.2", "cytoscape-popper": "^2.0.0", - "next": "^13.5.3", + "next": "^13.5.4", "next-transpile-modules": "^10.0.1", "react": "18.2.0", "react-chartjs-2": "^5.2.0", @@ -3548,9 +3548,9 @@ } }, "node_modules/@next/env": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.3.tgz", - "integrity": "sha512-X4te86vsbjsB7iO4usY9jLPtZ827Mbx+WcwNBGUOIuswuTAKQtzsuoxc/6KLxCMvogKG795MhrR1LDhYgDvasg==" + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.4.tgz", + "integrity": "sha512-LGegJkMvRNw90WWphGJ3RMHMVplYcOfRWf2Be3td3sUa+1AaxmsYyANsA+znrGCBjXJNi4XAQlSoEfUxs/4kIQ==" }, "node_modules/@next/eslint-plugin-next": { "version": "13.5.4", @@ -3582,9 +3582,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.3.tgz", - "integrity": "sha512-6hiYNJxJmyYvvKGrVThzo4nTcqvqUTA/JvKim7Auaj33NexDqSNwN5YrrQu+QhZJCIpv2tULSHt+lf+rUflLSw==", + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.4.tgz", + "integrity": "sha512-Df8SHuXgF1p+aonBMcDPEsaahNo2TCwuie7VXED4FVyECvdXfRT9unapm54NssV9tF3OQFKBFOdlje4T43VO0w==", "cpu": [ "arm64" ], @@ -3597,9 +3597,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.3.tgz", - "integrity": "sha512-UpBKxu2ob9scbpJyEq/xPgpdrgBgN3aLYlxyGqlYX5/KnwpJpFuIHU2lx8upQQ7L+MEmz+fA1XSgesoK92ppwQ==", + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.4.tgz", + "integrity": "sha512-siPuUwO45PnNRMeZnSa8n/Lye5ZX93IJom9wQRB5DEOdFrw0JjOMu1GINB8jAEdwa7Vdyn1oJ2xGNaQpdQQ9Pw==", "cpu": [ "x64" ], @@ -3612,9 +3612,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.3.tgz", - "integrity": "sha512-5AzM7Yx1Ky+oLY6pHs7tjONTF22JirDPd5Jw/3/NazJ73uGB05NqhGhB4SbeCchg7SlVYVBeRMrMSZwJwq/xoA==", + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.4.tgz", + "integrity": "sha512-l/k/fvRP/zmB2jkFMfefmFkyZbDkYW0mRM/LB+tH5u9pB98WsHXC0WvDHlGCYp3CH/jlkJPL7gN8nkTQVrQ/2w==", "cpu": [ "arm64" ], @@ -3627,9 +3627,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.3.tgz", - "integrity": "sha512-A/C1shbyUhj7wRtokmn73eBksjTM7fFQoY2v/0rTM5wehpkjQRLOXI8WJsag2uLhnZ4ii5OzR1rFPwoD9cvOgA==", + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.4.tgz", + "integrity": "sha512-YYGb7SlLkI+XqfQa8VPErljb7k9nUnhhRrVaOdfJNCaQnHBcvbT7cx/UjDQLdleJcfyg1Hkn5YSSIeVfjgmkTg==", "cpu": [ "arm64" ], @@ -3642,9 +3642,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.3.tgz", - "integrity": "sha512-FubPuw/Boz8tKkk+5eOuDHOpk36F80rbgxlx4+xty/U71e3wZZxVYHfZXmf0IRToBn1Crb8WvLM9OYj/Ur815g==", + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.4.tgz", + "integrity": "sha512-uE61vyUSClnCH18YHjA8tE1prr/PBFlBFhxBZis4XBRJoR+txAky5d7gGNUIbQ8sZZ7LVkSVgm/5Fc7mwXmRAg==", "cpu": [ "x64" ], @@ -3657,9 +3657,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.3.tgz", - "integrity": "sha512-DPw8nFuM1uEpbX47tM3wiXIR0Qa+atSzs9Q3peY1urkhofx44o7E1svnq+a5Q0r8lAcssLrwiM+OyJJgV/oj7g==", + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.4.tgz", + "integrity": "sha512-qVEKFYML/GvJSy9CfYqAdUexA6M5AklYcQCW+8JECmkQHGoPxCf04iMh7CPR7wkHyWWK+XLt4Ja7hhsPJtSnhg==", "cpu": [ "x64" ], @@ -3672,9 +3672,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.3.tgz", - "integrity": "sha512-zBPSP8cHL51Gub/YV8UUePW7AVGukp2D8JU93IHbVDu2qmhFAn9LWXiOOLKplZQKxnIPUkJTQAJDCWBWU4UWUA==", + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.4.tgz", + "integrity": "sha512-mDSQfqxAlfpeZOLPxLymZkX0hYF3juN57W6vFHTvwKlnHfmh12Pt7hPIRLYIShk8uYRsKPtMTth/EzpwRI+u8w==", "cpu": [ "arm64" ], @@ -3687,9 +3687,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.3.tgz", - "integrity": "sha512-ONcL/lYyGUj4W37D4I2I450SZtSenmFAvapkJQNIJhrPMhzDU/AdfLkW98NvH1D2+7FXwe7yclf3+B7v28uzBQ==", + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.4.tgz", + "integrity": "sha512-aoqAT2XIekIWoriwzOmGFAvTtVY5O7JjV21giozBTP5c6uZhpvTWRbmHXbmsjZqY4HnEZQRXWkSAppsIBweKqw==", "cpu": [ "ia32" ], @@ -3702,9 +3702,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.3.tgz", - "integrity": "sha512-2Vz2tYWaLqJvLcWbbTlJ5k9AN6JD7a5CN2pAeIzpbecK8ZF/yobA39cXtv6e+Z8c5UJuVOmaTldEAIxvsIux/Q==", + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.4.tgz", + "integrity": "sha512-cyRvlAxwlddlqeB9xtPSfNSCRy8BOa4wtMo0IuI9P7Y0XT2qpDrpFKRyZ7kUngZis59mPVla5k8X1oOJ8RxDYg==", "cpu": [ "x64" ], @@ -16982,18 +16982,17 @@ "dev": true }, "node_modules/next": { - "version": "13.5.3", - "resolved": "https://registry.npmjs.org/next/-/next-13.5.3.tgz", - "integrity": "sha512-4Nt4HRLYDW/yRpJ/QR2t1v63UOMS55A38dnWv3UDOWGezuY0ZyFO1ABNbD7mulVzs9qVhgy2+ppjdsANpKP1mg==", + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/next/-/next-13.5.4.tgz", + "integrity": "sha512-+93un5S779gho8y9ASQhb/bTkQF17FNQOtXLKAj3lsNgltEcF0C5PMLLncDmH+8X1EnJH1kbqAERa29nRXqhjA==", "dependencies": { - "@next/env": "13.5.3", + "@next/env": "13.5.4", "@swc/helpers": "0.5.2", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", + "postcss": "8.4.31", "styled-jsx": "5.1.1", - "watchpack": "2.4.0", - "zod": "3.21.4" + "watchpack": "2.4.0" }, "bin": { "next": "dist/bin/next" @@ -17002,15 +17001,15 @@ "node": ">=16.14.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "13.5.3", - "@next/swc-darwin-x64": "13.5.3", - "@next/swc-linux-arm64-gnu": "13.5.3", - "@next/swc-linux-arm64-musl": "13.5.3", - "@next/swc-linux-x64-gnu": "13.5.3", - "@next/swc-linux-x64-musl": "13.5.3", - "@next/swc-win32-arm64-msvc": "13.5.3", - "@next/swc-win32-ia32-msvc": "13.5.3", - "@next/swc-win32-x64-msvc": "13.5.3" + "@next/swc-darwin-arm64": "13.5.4", + "@next/swc-darwin-x64": "13.5.4", + "@next/swc-linux-arm64-gnu": "13.5.4", + "@next/swc-linux-arm64-musl": "13.5.4", + "@next/swc-linux-x64-gnu": "13.5.4", + "@next/swc-linux-x64-musl": "13.5.4", + "@next/swc-win32-arm64-msvc": "13.5.4", + "@next/swc-win32-ia32-msvc": "13.5.4", + "@next/swc-win32-x64-msvc": "13.5.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -17035,29 +17034,6 @@ "enhanced-resolve": "^5.10.0" } }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -17887,7 +17863,6 @@ "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -22366,14 +22341,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zod": { - "version": "3.21.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", - "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } } } diff --git a/dashboard/package.json b/dashboard/package.json index 0cf2aa411..7c20ad681 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -23,7 +23,7 @@ "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-node-html-label": "^1.2.2", "cytoscape-popper": "^2.0.0", - "next": "^13.5.3", + "next": "^13.5.4", "next-transpile-modules": "^10.0.1", "react": "18.2.0", "react-chartjs-2": "^5.2.0", From 94881a18422cdefd2fb9644b0bca00864b16ea76 Mon Sep 17 00:00:00 2001 From: Azanul Haque <42029519+Azanul@users.noreply.github.com> Date: Mon, 9 Oct 2023 17:41:36 +0530 Subject: [PATCH 028/113] remove OVH from README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 459a4a253..ce3a84c85 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Komiser is an open source project created to **analyse** and **manage cloud cost * Build an inventory of your cloud infrastructure assets. * Control your **resource usage** and gain visibility across all used services to achieve maximum cost-effectiveness. * Detect **potential vulnerabilities** that could put your cloud environment at risk. -* Get a deep understanding of **how you spend** on the AWS, Azure, GCP, Civo, OVH, DigitalOcean and OCI. +* Get a deep understanding of **how you spend** on the AWS, Azure, GCP, Civo, DigitalOcean and OCI. * Uncover idle and untagged resources, ensuring that no resource goes unnoticed.

Komiser dashboard

From 5a8c70ccd646945058c5b89bfa75a83ca6e50277 Mon Sep 17 00:00:00 2001 From: Ahmed Samir Date: Mon, 9 Oct 2023 16:49:28 +0300 Subject: [PATCH 029/113] feat: support k8s daemonsets --- providers/k8s/core/daemonsets.go | 77 ++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 providers/k8s/core/daemonsets.go diff --git a/providers/k8s/core/daemonsets.go b/providers/k8s/core/daemonsets.go new file mode 100644 index 000000000..9c25372bd --- /dev/null +++ b/providers/k8s/core/daemonsets.go @@ -0,0 +1,77 @@ +package core + +import ( + "context" + "time" + + log "github.com/sirupsen/logrus" + + "github.com/tailwarden/komiser/models" + "github.com/tailwarden/komiser/providers" + oc "github.com/tailwarden/komiser/providers/k8s/opencost" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func DaemonSets(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) { + resources := make([]models.Resource, 0) + + var config metav1.ListOptions + + opencostEnabled := true + daemonsetsCost, err := oc.GetOpencostInfo(client.K8sClient.OpencostBaseUrl, "daemonset") + if err != nil { + log.Errorf("ERROR: Couldn't get daemonsets info from OpenCost: %v", err) + log.Warn("Opencost disabled") + opencostEnabled = false + } + + for { + res, err := client.K8sClient.Client.AppsV1().DaemonSets("").List(ctx, config) + if err != nil { + return nil, err + } + + for _, daemonset := range res.Items { + tags := make([]models.Tag, 0) + + for key, value := range daemonset.Labels { + tags = append(tags, models.Tag{ + Key: key, + Value: value, + }) + } + + cost := 0.0 + if opencostEnabled { + cost = daemonsetsCost[daemonset.Name].TotalCost + } + + resources = append(resources, models.Resource{ + Provider: "Kubernetes", + Account: client.Name, + Service: "Daemonset", + ResourceId: string(daemonset.UID), + Name: daemonset.Name, + Region: daemonset.Namespace, + Cost: cost, + CreatedAt: daemonset.CreationTimestamp.Time, + FetchedAt: time.Now(), + Tags: tags, + }) + } + + if res.GetContinue() == "" { + break + } + + config.Continue = res.GetContinue() + } + + log.WithFields(log.Fields{ + "provider": "Kubernetes", + "account": client.Name, + "service": "Daemonset", + "resources": len(resources), + }).Info("Fetched resources") + return resources, nil +} From ce6ea10ba9feb3f9f5fb5302476e2fe95b541425 Mon Sep 17 00:00:00 2001 From: Ahmed Samir Date: Mon, 9 Oct 2023 16:57:04 +0300 Subject: [PATCH 030/113] feat: support k8s statefulsets Signed-off-by: Ahmed Samir --- providers/k8s/core/daemonsets.go | 4 +- providers/k8s/core/statefulsets.go | 77 ++++++++++++++++++++++++++++++ providers/k8s/k8s.go | 2 + 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 providers/k8s/core/statefulsets.go diff --git a/providers/k8s/core/daemonsets.go b/providers/k8s/core/daemonsets.go index 9c25372bd..0cff39cb7 100644 --- a/providers/k8s/core/daemonsets.go +++ b/providers/k8s/core/daemonsets.go @@ -49,7 +49,7 @@ func DaemonSets(ctx context.Context, client providers.ProviderClient) ([]models. resources = append(resources, models.Resource{ Provider: "Kubernetes", Account: client.Name, - Service: "Daemonset", + Service: "DaemonSet", ResourceId: string(daemonset.UID), Name: daemonset.Name, Region: daemonset.Namespace, @@ -70,7 +70,7 @@ func DaemonSets(ctx context.Context, client providers.ProviderClient) ([]models. log.WithFields(log.Fields{ "provider": "Kubernetes", "account": client.Name, - "service": "Daemonset", + "service": "DaemonSet", "resources": len(resources), }).Info("Fetched resources") return resources, nil diff --git a/providers/k8s/core/statefulsets.go b/providers/k8s/core/statefulsets.go new file mode 100644 index 000000000..3d0bb3bef --- /dev/null +++ b/providers/k8s/core/statefulsets.go @@ -0,0 +1,77 @@ +package core + +import ( + "context" + "time" + + log "github.com/sirupsen/logrus" + + "github.com/tailwarden/komiser/models" + "github.com/tailwarden/komiser/providers" + oc "github.com/tailwarden/komiser/providers/k8s/opencost" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func StatefulSets(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) { + resources := make([]models.Resource, 0) + + var config metav1.ListOptions + + opencostEnabled := true + statefulsetsCost, err := oc.GetOpencostInfo(client.K8sClient.OpencostBaseUrl, "statefulset") + if err != nil { + log.Errorf("ERROR: Couldn't get statefulsets info from OpenCost: %v", err) + log.Warn("Opencost disabled") + opencostEnabled = false + } + + for { + res, err := client.K8sClient.Client.AppsV1().StatefulSets("").List(ctx, config) + if err != nil { + return nil, err + } + + for _, statefulset := range res.Items { + tags := make([]models.Tag, 0) + + for key, value := range statefulset.Labels { + tags = append(tags, models.Tag{ + Key: key, + Value: value, + }) + } + + cost := 0.0 + if opencostEnabled { + cost = statefulsetsCost[statefulset.Name].TotalCost + } + + resources = append(resources, models.Resource{ + Provider: "Kubernetes", + Account: client.Name, + Service: "StatefulSet", + ResourceId: string(statefulset.UID), + Name: statefulset.Name, + Region: statefulset.Namespace, + Cost: cost, + CreatedAt: statefulset.CreationTimestamp.Time, + FetchedAt: time.Now(), + Tags: tags, + }) + } + + if res.GetContinue() == "" { + break + } + + config.Continue = res.GetContinue() + } + + log.WithFields(log.Fields{ + "provider": "Kubernetes", + "account": client.Name, + "service": "StatefulSet", + "resources": len(resources), + }).Info("Fetched resources") + return resources, nil +} diff --git a/providers/k8s/k8s.go b/providers/k8s/k8s.go index 3b52335b7..a5a87508d 100644 --- a/providers/k8s/k8s.go +++ b/providers/k8s/k8s.go @@ -22,6 +22,8 @@ func listOfSupportedServices() []providers.FetchDataFunction { core.ServiceAccounts, core.Nodes, core.Namespaces, + core.DaemonSets, + core.StatefulSets, } } From b15fafcf0686b471d7e3efa96307f47f3972d863 Mon Sep 17 00:00:00 2001 From: Ahmed Samir Date: Mon, 9 Oct 2023 16:59:14 +0300 Subject: [PATCH 031/113] feat: support k8s jobs Signed-off-by: Ahmed Samir --- providers/k8s/core/jobs.go | 77 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 providers/k8s/core/jobs.go diff --git a/providers/k8s/core/jobs.go b/providers/k8s/core/jobs.go new file mode 100644 index 000000000..5303f4d53 --- /dev/null +++ b/providers/k8s/core/jobs.go @@ -0,0 +1,77 @@ +package core + +import ( + "context" + "time" + + log "github.com/sirupsen/logrus" + + "github.com/tailwarden/komiser/models" + "github.com/tailwarden/komiser/providers" + oc "github.com/tailwarden/komiser/providers/k8s/opencost" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func Jobs(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) { + resources := make([]models.Resource, 0) + + var config metav1.ListOptions + + opencostEnabled := true + jobsCost, err := oc.GetOpencostInfo(client.K8sClient.OpencostBaseUrl, "job") + if err != nil { + log.Errorf("ERROR: Couldn't get jobs info from OpenCost: %v", err) + log.Warn("Opencost disabled") + opencostEnabled = false + } + + for { + res, err := client.K8sClient.Client.BatchV1().Jobs("").List(ctx, config) + if err != nil { + return nil, err + } + + for _, job := range res.Items { + tags := make([]models.Tag, 0) + + for key, value := range job.Labels { + tags = append(tags, models.Tag{ + Key: key, + Value: value, + }) + } + + cost := 0.0 + if opencostEnabled { + cost = jobsCost[job.Name].TotalCost + } + + resources = append(resources, models.Resource{ + Provider: "Kubernetes", + Account: client.Name, + Service: "Job", + ResourceId: string(job.UID), + Name: job.Name, + Region: job.Namespace, + Cost: cost, + CreatedAt: job.CreationTimestamp.Time, + FetchedAt: time.Now(), + Tags: tags, + }) + } + + if res.GetContinue() == "" { + break + } + + config.Continue = res.GetContinue() + } + + log.WithFields(log.Fields{ + "provider": "Kubernetes", + "account": client.Name, + "service": "DaemonSet", + "resources": len(resources), + }).Info("Fetched resources") + return resources, nil +} From 879122ee235b398e3d50de9d866a98822653d37d Mon Sep 17 00:00:00 2001 From: Ahmed Samir Date: Mon, 9 Oct 2023 17:00:17 +0300 Subject: [PATCH 032/113] feat: add jobs to supported list Signed-off-by: Ahmed Samir --- providers/k8s/k8s.go | 1 + 1 file changed, 1 insertion(+) diff --git a/providers/k8s/k8s.go b/providers/k8s/k8s.go index a5a87508d..60226960b 100644 --- a/providers/k8s/k8s.go +++ b/providers/k8s/k8s.go @@ -24,6 +24,7 @@ func listOfSupportedServices() []providers.FetchDataFunction { core.Namespaces, core.DaemonSets, core.StatefulSets, + core.Jobs, } } From eeef8dc834823ed721313a5ec94edec514dbca99 Mon Sep 17 00:00:00 2001 From: bishal7679 Date: Mon, 9 Oct 2023 19:44:21 +0530 Subject: [PATCH 033/113] modified for dynamic charges Signed-off-by: bishal7679 --- providers/aws/s3/buckets.go | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/providers/aws/s3/buckets.go b/providers/aws/s3/buckets.go index eb42a941c..8d5030d56 100644 --- a/providers/aws/s3/buckets.go +++ b/providers/aws/s3/buckets.go @@ -38,11 +38,6 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { Value: aws.String(client.AWSClient.Region), Type: types.FilterTypeTermMatch, }, - { - Field: aws.String("productFamily"), - Value: aws.String("Storage"), - Type: types.FilterTypeTermMatch, - }, }, }) if err != nil { @@ -74,10 +69,6 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { Name: aws.String("BucketName"), Value: bucket.Name, }, - { - Name: aws.String("StorageType"), - Value: aws.String("StandardStorage"), - }, }, Unit: cloudwatchTypes.StandardUnitBytes, Period: aws.Int32(3600), @@ -94,15 +85,6 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { } sizeInTB := ConvertBytesToTerabytes(int64(bucketSize)) - storageCostPerGB := 0.0 - - if sizeInTB <= 50 { - storageCostPerGB = (sizeInTB * 1024) * 0.023 - } else if sizeInTB <= 450 { - storageCostPerGB = (sizeInTB * 1024) * 0.022 - } else { - storageCostPerGB = (sizeInTB * 1024) * 0.021 - } // metrics for bucket usage @@ -116,10 +98,6 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { Name: aws.String("BucketName"), Value: bucket.Name, }, - { - Name: aws.String("StorageType"), - Value: aws.String("StandardStorage"), - }, }, Unit: cloudwatchTypes.StandardUnitCount, Period: aws.Int32(3600), @@ -139,8 +117,9 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { monthlyCost := 0.0 + storageCharges := awsUtils.GetCost(priceMap["AWS-S3-Storage"], sizeInTB*1024) // charges per GB requestCharges := awsUtils.GetCost(priceMap["AWS-S3-Requests"], requestCount/1000) // charges per 1000 request - monthlyCost = storageCostPerGB + requestCharges + monthlyCost = storageCharges + requestCharges tagsResp, err := s3Client.GetBucketTagging(context.Background(), &s3.GetBucketTaggingInput{ Bucket: bucket.Name, }) From 90963c45953edfd323361817b7f63e8e0f613cdd Mon Sep 17 00:00:00 2001 From: titanventura Date: Mon, 9 Oct 2023 21:22:12 +0530 Subject: [PATCH 034/113] add. concurrent resource fetching code --- internal/internal.go | 34 +++++++++++++++++++--------------- providers/aws/aws.go | 34 ++++++++++++++++++---------------- providers/providers.go | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 31 deletions(-) diff --git a/internal/internal.go b/internal/internal.go index 1765a1171..48b966938 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -79,10 +79,14 @@ func Exec(address string, port int, configPath string, telemetry bool, a utils.A _, err = cron.Every(1).Hours().Do(func() { log.Info("Fetching resources workflow has started") - err = fetchResources(ctx, clients, regions, telemetry) + + numWorkers := 64 + wp := providers.NewWorkerPool(numWorkers) + err = fetchResources(ctx, clients, regions, telemetry, wp) if err != nil { log.Fatal(err) } + wp.Wait() }) if err != nil { @@ -209,7 +213,7 @@ func setupDBConnection(c *models.Config) error { return nil } -func triggerFetchingWorfklow(ctx context.Context, client providers.ProviderClient, provider string, telemetry bool, regions []string) { +func triggerFetchingWorfklow(ctx context.Context, client providers.ProviderClient, provider string, telemetry bool, regions []string, wp *providers.WorkerPool) { localHub := sentry.CurrentHub().Clone() defer func() { @@ -233,7 +237,7 @@ func triggerFetchingWorfklow(ctx context.Context, client providers.ProviderClien switch provider { case "AWS": - aws.FetchResources(ctx, client, regions, db, telemetry, analytics) + aws.FetchResources(ctx, client, regions, db, telemetry, analytics, wp) case "DigitalOcean": do.FetchResources(ctx, client, db, telemetry, analytics) case "OCI": @@ -257,30 +261,30 @@ func triggerFetchingWorfklow(ctx context.Context, client providers.ProviderClien } } -func fetchResources(ctx context.Context, clients []providers.ProviderClient, regions []string, telemetry bool) error { +func fetchResources(ctx context.Context, clients []providers.ProviderClient, regions []string, telemetry bool, wp *providers.WorkerPool) error { for _, client := range clients { if client.AWSClient != nil { - go triggerFetchingWorfklow(ctx, client, "AWS", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "AWS", telemetry, regions, wp) } else if client.DigitalOceanClient != nil { - go triggerFetchingWorfklow(ctx, client, "DigitalOcean", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "DigitalOcean", telemetry, regions, wp) } else if client.OciClient != nil { - go triggerFetchingWorfklow(ctx, client, "OCI", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "OCI", telemetry, regions, wp) } else if client.CivoClient != nil { - go triggerFetchingWorfklow(ctx, client, "Civo", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "Civo", telemetry, regions, wp) } else if client.K8sClient != nil { - go triggerFetchingWorfklow(ctx, client, "Kubernetes", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "Kubernetes", telemetry, regions, wp) } else if client.LinodeClient != nil { - go triggerFetchingWorfklow(ctx, client, "Linode", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "Linode", telemetry, regions, wp) } else if client.TencentClient != nil { - go triggerFetchingWorfklow(ctx, client, "Tencent", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "Tencent", telemetry, regions, wp) } else if client.AzureClient != nil { - go triggerFetchingWorfklow(ctx, client, "Azure", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "Azure", telemetry, regions, wp) } else if client.ScalewayClient != nil { - go triggerFetchingWorfklow(ctx, client, "Scaleway", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "Scaleway", telemetry, regions, wp) } else if client.MongoDBAtlasClient != nil { - go triggerFetchingWorfklow(ctx, client, "MongoDBAtlas", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "MongoDBAtlas", telemetry, regions, wp) } else if client.GCPClient != nil { - go triggerFetchingWorfklow(ctx, client, "GCP", telemetry, regions) + go triggerFetchingWorfklow(ctx, client, "GCP", telemetry, regions, wp) } } return nil diff --git a/providers/aws/aws.go b/providers/aws/aws.go index ecf1e862e..7b1cc8f4d 100644 --- a/providers/aws/aws.go +++ b/providers/aws/aws.go @@ -98,7 +98,7 @@ func listOfSupportedServices() []providers.FetchDataFunction { } } -func FetchResources(ctx context.Context, client providers.ProviderClient, regions []string, db *bun.DB, telemetry bool, analytics utils.Analytics) { +func FetchResources(ctx context.Context, client providers.ProviderClient, regions []string, db *bun.DB, telemetry bool, analytics utils.Analytics, wp *providers.WorkerPool) { listOfSupportedRegions := getRegions() if len(regions) > 0 { log.Infof("Komiser will fetch resources from the following regions: %s", strings.Join(regions, ",")) @@ -108,23 +108,25 @@ func FetchResources(ctx context.Context, client providers.ProviderClient, region for _, region := range listOfSupportedRegions { client.AWSClient.Region = region for _, fetchResources := range listOfSupportedServices() { - resources, err := fetchResources(ctx, client) - if err != nil { - log.Warnf("[%s][AWS] %s", client.Name, err) - } else { - for _, resource := range resources { - _, err = db.NewInsert().Model(&resource).On("CONFLICT (resource_id) DO UPDATE").Set("cost = EXCLUDED.cost, relations=EXCLUDED.relations").Exec(context.Background()) - if err != nil { - log.WithError(err).Errorf("db trigger failed") + wp.SubmitTask(func() { + resources, err := fetchResources(ctx, client) + if err != nil { + log.Warnf("[%s][AWS] %s", client.Name, err) + } else { + for _, resource := range resources { + _, err = db.NewInsert().Model(&resource).On("CONFLICT (resource_id) DO UPDATE").Set("cost = EXCLUDED.cost, relations=EXCLUDED.relations").Exec(context.Background()) + if err != nil { + log.WithError(err).Errorf("db trigger failed") + } + } + if telemetry { + analytics.TrackEvent("discovered_resources", map[string]interface{}{ + "provider": "AWS", + "resources": len(resources), + }) } } - if telemetry { - analytics.TrackEvent("discovered_resources", map[string]interface{}{ - "provider": "AWS", - "resources": len(resources), - }) - } - } + }) } } } diff --git a/providers/providers.go b/providers/providers.go index 138fa34da..9e2b1dc22 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -2,6 +2,7 @@ package providers import ( "context" + "sync" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/aws/aws-sdk-go-v2/aws" @@ -47,3 +48,39 @@ type K8sClient struct { Client *kubernetes.Clientset OpencostBaseUrl string } + +type WorkerPool struct { + numWorkers int + tasks chan func() + wg sync.WaitGroup +} + +func NewWorkerPool(numWorkers int) *WorkerPool { + return &WorkerPool{ + numWorkers: numWorkers, + tasks: make(chan func()), + } +} + +func (wp *WorkerPool) Start() { + for i := 0; i < wp.numWorkers; i++ { + wp.wg.Add(1) + go wp.worker() + } +} + +func (wp *WorkerPool) SubmitTask(task func()) { + wp.tasks <- task +} + +func (wp *WorkerPool) Wait() { + close(wp.tasks) + wp.wg.Wait() +} + +func (wp *WorkerPool) worker() { + defer wp.wg.Done() + for task := range wp.tasks { + task() + } +} From 66b6a6c180060397f66c1f6334a279de1f0e6b88 Mon Sep 17 00:00:00 2001 From: bishal7679 Date: Mon, 9 Oct 2023 23:13:41 +0530 Subject: [PATCH 035/113] removed unneccesary comments Signed-off-by: bishal7679 --- providers/aws/s3/buckets.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/providers/aws/s3/buckets.go b/providers/aws/s3/buckets.go index 8d5030d56..cc3678020 100644 --- a/providers/aws/s3/buckets.go +++ b/providers/aws/s3/buckets.go @@ -34,7 +34,7 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { ServiceCode: aws.String("AmazonS3"), Filters: []types.Filter{ { - Field: aws.String("regionCode"), // Filter by region + Field: aws.String("regionCode"), Value: aws.String(client.AWSClient.Region), Type: types.FilterTypeTermMatch, }, @@ -51,14 +51,12 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { return resources, err } - // --------------------------------------------------------------- output, err := s3Client.ListBuckets(context.Background(), &config) if err != nil { return resources, err } for _, bucket := range output.Buckets { - // metrics for bucket size metricsBucketSizebytesOutput, err := cloudwatchClient.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{ StartTime: aws.Time(utils.BeginningOfMonth(time.Now())), EndTime: aws.Time(time.Now()), @@ -86,8 +84,6 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { sizeInTB := ConvertBytesToTerabytes(int64(bucketSize)) - // metrics for bucket usage - metricsUsageOutput, err := cloudwatchClient.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{ StartTime: aws.Time(utils.BeginningOfMonth(time.Now())), EndTime: aws.Time(time.Now()), @@ -113,12 +109,11 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { if metricsUsageOutput != nil && len(metricsUsageOutput.Datapoints) > 0 { requestCount = *metricsUsageOutput.Datapoints[0].Sum } - // requestCost := (requestCount / 1000) * 0.0004 monthlyCost := 0.0 - storageCharges := awsUtils.GetCost(priceMap["AWS-S3-Storage"], sizeInTB*1024) // charges per GB - requestCharges := awsUtils.GetCost(priceMap["AWS-S3-Requests"], requestCount/1000) // charges per 1000 request + storageCharges := awsUtils.GetCost(priceMap["AWS-S3-Storage"], sizeInTB*1024) + requestCharges := awsUtils.GetCost(priceMap["AWS-S3-Requests"], requestCount/1000) monthlyCost = storageCharges + requestCharges tagsResp, err := s3Client.GetBucketTagging(context.Background(), &s3.GetBucketTaggingInput{ Bucket: bucket.Name, From 334e8049da55d4192b06c7756382a2c3f0b349d4 Mon Sep 17 00:00:00 2001 From: bishal7679 Date: Mon, 9 Oct 2023 23:15:22 +0530 Subject: [PATCH 036/113] added key for pricemap Signed-off-by: bishal7679 --- providers/aws/s3/buckets.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/aws/s3/buckets.go b/providers/aws/s3/buckets.go index cc3678020..de60e6ed6 100644 --- a/providers/aws/s3/buckets.go +++ b/providers/aws/s3/buckets.go @@ -113,7 +113,7 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) { monthlyCost := 0.0 storageCharges := awsUtils.GetCost(priceMap["AWS-S3-Storage"], sizeInTB*1024) - requestCharges := awsUtils.GetCost(priceMap["AWS-S3-Requests"], requestCount/1000) + requestCharges := awsUtils.GetCost(priceMap["S3-API-Tier1"], requestCount/1000) monthlyCost = storageCharges + requestCharges tagsResp, err := s3Client.GetBucketTagging(context.Background(), &s3.GetBucketTaggingInput{ Bucket: bucket.Name, From 3599f1f22e0dd30e5edd499c4752ea775b4ddc5a Mon Sep 17 00:00:00 2001 From: titanventura Date: Tue, 10 Oct 2023 00:35:10 +0530 Subject: [PATCH 037/113] feat(concurrency): fix - waiting and waitgroup logic --- internal/internal.go | 32 +++++++++++++++++++++----------- providers/providers.go | 8 ++++---- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/internal/internal.go b/internal/internal.go index 48b966938..2e3288c09 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -82,6 +82,7 @@ func Exec(address string, port int, configPath string, telemetry bool, a utils.A numWorkers := 64 wp := providers.NewWorkerPool(numWorkers) + wp.Start() err = fetchResources(ctx, clients, regions, telemetry, wp) if err != nil { log.Fatal(err) @@ -262,29 +263,38 @@ func triggerFetchingWorfklow(ctx context.Context, client providers.ProviderClien } func fetchResources(ctx context.Context, clients []providers.ProviderClient, regions []string, telemetry bool, wp *providers.WorkerPool) error { + + workflowTrigger := func(client providers.ProviderClient, provider string) { + wp.Wg.Add(1) + go func() { + defer wp.Wg.Done() + triggerFetchingWorfklow(ctx, client, provider, telemetry, regions, wp) + }() + } + for _, client := range clients { if client.AWSClient != nil { - go triggerFetchingWorfklow(ctx, client, "AWS", telemetry, regions, wp) + workflowTrigger(client, "AWS") } else if client.DigitalOceanClient != nil { - go triggerFetchingWorfklow(ctx, client, "DigitalOcean", telemetry, regions, wp) + workflowTrigger(client, "DigitalOcean") } else if client.OciClient != nil { - go triggerFetchingWorfklow(ctx, client, "OCI", telemetry, regions, wp) + workflowTrigger(client, "OCI") } else if client.CivoClient != nil { - go triggerFetchingWorfklow(ctx, client, "Civo", telemetry, regions, wp) + workflowTrigger(client, "Civo") } else if client.K8sClient != nil { - go triggerFetchingWorfklow(ctx, client, "Kubernetes", telemetry, regions, wp) + workflowTrigger(client, "Kubernetes") } else if client.LinodeClient != nil { - go triggerFetchingWorfklow(ctx, client, "Linode", telemetry, regions, wp) + workflowTrigger(client, "Linode") } else if client.TencentClient != nil { - go triggerFetchingWorfklow(ctx, client, "Tencent", telemetry, regions, wp) + workflowTrigger(client, "Tencent") } else if client.AzureClient != nil { - go triggerFetchingWorfklow(ctx, client, "Azure", telemetry, regions, wp) + workflowTrigger(client, "Azure") } else if client.ScalewayClient != nil { - go triggerFetchingWorfklow(ctx, client, "Scaleway", telemetry, regions, wp) + workflowTrigger(client, "Scaleway") } else if client.MongoDBAtlasClient != nil { - go triggerFetchingWorfklow(ctx, client, "MongoDBAtlas", telemetry, regions, wp) + workflowTrigger(client, "MongoDBAtlas") } else if client.GCPClient != nil { - go triggerFetchingWorfklow(ctx, client, "GCP", telemetry, regions, wp) + workflowTrigger(client, "GCP") } } return nil diff --git a/providers/providers.go b/providers/providers.go index 9e2b1dc22..81afb7b62 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -52,7 +52,7 @@ type K8sClient struct { type WorkerPool struct { numWorkers int tasks chan func() - wg sync.WaitGroup + Wg sync.WaitGroup } func NewWorkerPool(numWorkers int) *WorkerPool { @@ -64,23 +64,23 @@ func NewWorkerPool(numWorkers int) *WorkerPool { func (wp *WorkerPool) Start() { for i := 0; i < wp.numWorkers; i++ { - wp.wg.Add(1) go wp.worker() } } func (wp *WorkerPool) SubmitTask(task func()) { + wp.Wg.Add(1) wp.tasks <- task } func (wp *WorkerPool) Wait() { + wp.Wg.Wait() close(wp.tasks) - wp.wg.Wait() } func (wp *WorkerPool) worker() { - defer wp.wg.Done() for task := range wp.tasks { task() + wp.Wg.Done() } } From c1b8c319e8dd6f9be1ada7ba71a62ea00bc26ddd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 23:23:43 +0000 Subject: [PATCH 038/113] chore(deps-dev): bump @storybook/react from 7.4.3 to 7.4.6 in /dashboard Bumps [@storybook/react](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/react) from 7.4.3 to 7.4.6. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v7.4.6/code/renderers/react) --- updated-dependencies: - dependency-name: "@storybook/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dashboard/package-lock.json | 399 ++++++++++++++++++++++++++++++++++-- dashboard/package.json | 2 +- 2 files changed, 385 insertions(+), 16 deletions(-) diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 44c5dde14..84daa9da7 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -36,7 +36,7 @@ "@storybook/addons": "^7.0.0-beta.52", "@storybook/blocks": "^7.4.5", "@storybook/nextjs": "^7.0.0-alpha.41", - "@storybook/react": "^7.0.0-beta.52", + "@storybook/react": "^7.4.6", "@storybook/testing-library": "^0.2.2", "@storybook/theming": "^7.4.5", "@testing-library/jest-dom": "^5.16.5", @@ -6201,13 +6201,99 @@ } }, "node_modules/@storybook/core-client": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.4.3.tgz", - "integrity": "sha512-YRt07TxC+HUtnyvbpJbY8d2+2QfFExBL7zRbms9tIRorddWfPBq+lA2RS9zcjUJJJtNSz1+ST70FuGr1N3AXGg==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.4.6.tgz", + "integrity": "sha512-tfgxAHeCvMcs6DsVgtb4hQSDaCHeAPJOsoyhb47eDQfk4OmxzriM0qWucJV5DePSMi+KutX/rN2u0JxfOuN68g==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.3", - "@storybook/preview-api": "7.4.3" + "@storybook/client-logger": "7.4.6", + "@storybook/preview-api": "7.4.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/core-client/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/core-client/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/core-client/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/core-client/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/core-client/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" }, "funding": { "type": "opencollective", @@ -6735,6 +6821,66 @@ } } }, + "node_modules/@storybook/nextjs/node_modules/@storybook/core-client": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.4.3.tgz", + "integrity": "sha512-YRt07TxC+HUtnyvbpJbY8d2+2QfFExBL7zRbms9tIRorddWfPBq+lA2RS9zcjUJJJtNSz1+ST70FuGr1N3AXGg==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.3", + "@storybook/preview-api": "7.4.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/nextjs/node_modules/@storybook/react": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-7.4.3.tgz", + "integrity": "sha512-koF1GLPBY5rm8t+6i70Iw6Ep/6T2C+XAlnP1dO/ZJAv8mmeQmOw+Kwh6nNPG8bthm0l1nImgqRw2ZwCD2AFoSA==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.3", + "@storybook/core-client": "7.4.3", + "@storybook/docs-tools": "7.4.3", + "@storybook/global": "^5.0.0", + "@storybook/preview-api": "7.4.3", + "@storybook/react-dom-shim": "7.4.3", + "@storybook/types": "7.4.3", + "@types/escodegen": "^0.0.6", + "@types/estree": "^0.0.51", + "@types/node": "^16.0.0", + "acorn": "^7.4.1", + "acorn-jsx": "^5.3.1", + "acorn-walk": "^7.2.0", + "escodegen": "^2.1.0", + "html-tags": "^3.1.0", + "lodash": "^4.17.21", + "prop-types": "^15.7.2", + "react-element-to-jsx-string": "^15.0.0", + "ts-dedent": "^2.0.0", + "type-fest": "~2.19", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@storybook/nextjs/node_modules/@types/node": { "version": "16.18.53", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.53.tgz", @@ -6805,6 +6951,66 @@ } } }, + "node_modules/@storybook/preset-react-webpack/node_modules/@storybook/core-client": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.4.3.tgz", + "integrity": "sha512-YRt07TxC+HUtnyvbpJbY8d2+2QfFExBL7zRbms9tIRorddWfPBq+lA2RS9zcjUJJJtNSz1+ST70FuGr1N3AXGg==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.3", + "@storybook/preview-api": "7.4.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/preset-react-webpack/node_modules/@storybook/react": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-7.4.3.tgz", + "integrity": "sha512-koF1GLPBY5rm8t+6i70Iw6Ep/6T2C+XAlnP1dO/ZJAv8mmeQmOw+Kwh6nNPG8bthm0l1nImgqRw2ZwCD2AFoSA==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.3", + "@storybook/core-client": "7.4.3", + "@storybook/docs-tools": "7.4.3", + "@storybook/global": "^5.0.0", + "@storybook/preview-api": "7.4.3", + "@storybook/react-dom-shim": "7.4.3", + "@storybook/types": "7.4.3", + "@types/escodegen": "^0.0.6", + "@types/estree": "^0.0.51", + "@types/node": "^16.0.0", + "acorn": "^7.4.1", + "acorn-jsx": "^5.3.1", + "acorn-walk": "^7.2.0", + "escodegen": "^2.1.0", + "html-tags": "^3.1.0", + "lodash": "^4.17.21", + "prop-types": "^15.7.2", + "react-element-to-jsx-string": "^15.0.0", + "ts-dedent": "^2.0.0", + "type-fest": "~2.19", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@storybook/preset-react-webpack/node_modules/@types/node": { "version": "16.18.53", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.53.tgz", @@ -6848,18 +7054,18 @@ } }, "node_modules/@storybook/react": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-7.4.3.tgz", - "integrity": "sha512-koF1GLPBY5rm8t+6i70Iw6Ep/6T2C+XAlnP1dO/ZJAv8mmeQmOw+Kwh6nNPG8bthm0l1nImgqRw2ZwCD2AFoSA==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-7.4.6.tgz", + "integrity": "sha512-w0dVo64baFFPTGpUOWFqkKsu6pQincoymegSNgqaBd5DxEyMDRiRoTWSJHMKE9BwgE8SyWhRkP1ak1mkccSOhQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.3", - "@storybook/core-client": "7.4.3", - "@storybook/docs-tools": "7.4.3", + "@storybook/client-logger": "7.4.6", + "@storybook/core-client": "7.4.6", + "@storybook/docs-tools": "7.4.6", "@storybook/global": "^5.0.0", - "@storybook/preview-api": "7.4.3", - "@storybook/react-dom-shim": "7.4.3", - "@storybook/types": "7.4.3", + "@storybook/preview-api": "7.4.6", + "@storybook/react-dom-shim": "7.4.6", + "@storybook/types": "7.4.6", "@types/escodegen": "^0.0.6", "@types/estree": "^0.0.51", "@types/node": "^16.0.0", @@ -6926,6 +7132,169 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@storybook/react/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/react/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/react/node_modules/@storybook/core-common": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.4.6.tgz", + "integrity": "sha512-05MJFmOM86qvTLtgDskokIFz9txe0Lbhq4L3by1FtF0GwgH+p+W6I94KI7c6ANER+kVZkXQZhiRzwBFnVTW+Cg==", + "dev": true, + "dependencies": { + "@storybook/core-events": "7.4.6", + "@storybook/node-logger": "7.4.6", + "@storybook/types": "7.4.6", + "@types/find-cache-dir": "^3.2.1", + "@types/node": "^16.0.0", + "@types/node-fetch": "^2.6.4", + "@types/pretty-hrtime": "^1.0.0", + "chalk": "^4.1.0", + "esbuild": "^0.18.0", + "esbuild-register": "^3.4.0", + "file-system-cache": "2.3.0", + "find-cache-dir": "^3.0.0", + "find-up": "^5.0.0", + "fs-extra": "^11.1.0", + "glob": "^10.0.0", + "handlebars": "^4.7.7", + "lazy-universal-dotenv": "^4.0.0", + "node-fetch": "^2.0.0", + "picomatch": "^2.3.0", + "pkg-dir": "^5.0.0", + "pretty-hrtime": "^1.0.3", + "resolve-from": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/react/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/react/node_modules/@storybook/docs-tools": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.4.6.tgz", + "integrity": "sha512-nZj1L/8WwKWWJ41FW4MaKGajZUtrhnr9UwflRCkQJaWhAKmDfOb5M5TqI93uCOULpFPOm5wpoMBz2IHInQ2Lrg==", + "dev": true, + "dependencies": { + "@storybook/core-common": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/types": "7.4.6", + "@types/doctrine": "^0.0.3", + "doctrine": "^3.0.0", + "lodash": "^4.17.21" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/react/node_modules/@storybook/node-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.4.6.tgz", + "integrity": "sha512-djZb310Q27GviDug1XBv0jOEDLCiwr4hhDE0aifCEKZpfNCi/EaP31nbWimFzZwxu4hE/YAPWExzScruR1zw9Q==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/react/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/react/node_modules/@storybook/react-dom-shim": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.4.6.tgz", + "integrity": "sha512-DSq8l9FDocUF1ooVI+TF83pddj1LynE/Hv0/y8XZhc3IgJ/HkuOQuUmfz29ezgfAi9gFYUR8raTIBi3/xdoRmw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/react/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, "node_modules/@storybook/react/node_modules/@types/node": { "version": "16.18.53", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.53.tgz", diff --git a/dashboard/package.json b/dashboard/package.json index 7c20ad681..11d562a2f 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -42,7 +42,7 @@ "@storybook/addons": "^7.0.0-beta.52", "@storybook/blocks": "^7.4.5", "@storybook/nextjs": "^7.0.0-alpha.41", - "@storybook/react": "^7.0.0-beta.52", + "@storybook/react": "^7.4.6", "@storybook/testing-library": "^0.2.2", "@storybook/theming": "^7.4.5", "@testing-library/jest-dom": "^5.16.5", From 493845ddeac6eab63f3a7f15dd7876937a725a16 Mon Sep 17 00:00:00 2001 From: Azanul Haque <42029519+Azanul@users.noreply.github.com> Date: Tue, 10 Oct 2023 06:30:56 +0000 Subject: [PATCH 039/113] using a separate WaitGroup Signed-off-by: Azanul Haque <42029519+Azanul@users.noreply.github.com> --- internal/internal.go | 10 +++++++--- providers/providers.go | 8 ++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/internal/internal.go b/internal/internal.go index 2e3288c09..1c55b6de5 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -11,6 +11,7 @@ import ( "runtime" "strconv" "strings" + "sync" "time" "github.com/getsentry/sentry-go" @@ -54,7 +55,7 @@ var Arch = runtime.GOARCH var db *bun.DB var analytics utils.Analytics -func Exec(address string, port int, configPath string, telemetry bool, a utils.Analytics, regions []string, cmd *cobra.Command) error { +func Exec(address string, port int, configPath string, telemetry bool, a utils.Analytics, regions []string, _ *cobra.Command) error { analytics = a ctx := context.Background() @@ -264,10 +265,11 @@ func triggerFetchingWorfklow(ctx context.Context, client providers.ProviderClien func fetchResources(ctx context.Context, clients []providers.ProviderClient, regions []string, telemetry bool, wp *providers.WorkerPool) error { + var wwg sync.WaitGroup workflowTrigger := func(client providers.ProviderClient, provider string) { - wp.Wg.Add(1) + wwg.Add(1) go func() { - defer wp.Wg.Done() + defer wwg.Done() triggerFetchingWorfklow(ctx, client, provider, telemetry, regions, wp) }() } @@ -297,6 +299,8 @@ func fetchResources(ctx context.Context, clients []providers.ProviderClient, reg workflowTrigger(client, "GCP") } } + + wwg.Wait() return nil } diff --git a/providers/providers.go b/providers/providers.go index 81afb7b62..ef0198714 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -52,7 +52,7 @@ type K8sClient struct { type WorkerPool struct { numWorkers int tasks chan func() - Wg sync.WaitGroup + wg sync.WaitGroup } func NewWorkerPool(numWorkers int) *WorkerPool { @@ -69,18 +69,18 @@ func (wp *WorkerPool) Start() { } func (wp *WorkerPool) SubmitTask(task func()) { - wp.Wg.Add(1) + wp.wg.Add(1) wp.tasks <- task } func (wp *WorkerPool) Wait() { - wp.Wg.Wait() + wp.wg.Wait() close(wp.tasks) } func (wp *WorkerPool) worker() { for task := range wp.tasks { task() - wp.Wg.Done() + wp.wg.Done() } } From 4394306740be38c38f1bfd28ed4456621dfc1a9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:34:08 +0000 Subject: [PATCH 040/113] chore(deps-dev): bump @types/react-dom in /dashboard Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 18.2.7 to 18.2.12. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) --- updated-dependencies: - dependency-name: "@types/react-dom" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dashboard/package-lock.json | 8 ++++---- dashboard/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 84daa9da7..1c777d710 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -47,7 +47,7 @@ "@types/node": "20.7.0", "@types/react": "18.2.22", "@types/react-cytoscapejs": "^1.2.3", - "@types/react-dom": "18.2.7", + "@types/react-dom": "18.2.12", "@types/react-simple-maps": "^3.0.0", "autoprefixer": "^10.4.13", "eslint": "^8.49.0", @@ -8180,9 +8180,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", - "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "version": "18.2.12", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.12.tgz", + "integrity": "sha512-QWZuiA/7J/hPIGocXreCRbx7wyoeet9ooxfbSA+zbIWqyQEE7GMtRn4A37BdYyksnN+/NDnWgfxZH9UVGDw1hg==", "dev": true, "dependencies": { "@types/react": "*" diff --git a/dashboard/package.json b/dashboard/package.json index 11d562a2f..d272243ca 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -53,7 +53,7 @@ "@types/node": "20.7.0", "@types/react": "18.2.22", "@types/react-cytoscapejs": "^1.2.3", - "@types/react-dom": "18.2.7", + "@types/react-dom": "18.2.12", "@types/react-simple-maps": "^3.0.0", "autoprefixer": "^10.4.13", "eslint": "^8.49.0", From 30c9657fd05256d00b9b2ac30807772a24afadb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:34:13 +0000 Subject: [PATCH 041/113] chore(deps-dev): bump @storybook/addon-essentials in /dashboard Bumps [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) from 7.4.3 to 7.4.6. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v7.4.6/code/addons/essentials) --- updated-dependencies: - dependency-name: "@storybook/addon-essentials" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dashboard/package-lock.json | 2226 ++++++++++++++++++++++++++++------- dashboard/package.json | 2 +- 2 files changed, 1804 insertions(+), 424 deletions(-) diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 84daa9da7..82194d6b1 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -30,7 +30,7 @@ "@commitlint/cli": "^17.2.0", "@commitlint/config-conventional": "^17.2.0", "@jest/globals": "^29.5.0", - "@storybook/addon-essentials": "^7.0.0-beta.52", + "@storybook/addon-essentials": "^7.4.6", "@storybook/addon-interactions": "^7.0.0-beta.52", "@storybook/addon-links": "^7.0.0-beta.52", "@storybook/addons": "^7.0.0-beta.52", @@ -4693,19 +4693,19 @@ } }, "node_modules/@storybook/addon-backgrounds": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.4.3.tgz", - "integrity": "sha512-NCcJKbz/kVSOXmoV1c+YoM28/oG9oO/kv1xwtX//cVv02SGerRCRqwB7zt0NzcLMSkrwaphRuXd55n0J7nGrBg==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.4.6.tgz", + "integrity": "sha512-+LHTZB/ZYMAzkyD5ZxSriBsqmsrvIaW/Nnd/BeuXGbkrVKKqM0qAKiFZAfjc2WchA1piVNy0/1Rsf+kuYCEiJw==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.3", - "@storybook/components": "7.4.3", - "@storybook/core-events": "7.4.3", + "@storybook/client-logger": "7.4.6", + "@storybook/components": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/theming": "7.4.3", - "@storybook/types": "7.4.3", + "@storybook/manager-api": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", "memoizerific": "^1.11.3", "ts-dedent": "^2.0.0" }, @@ -4726,90 +4726,52 @@ } } }, - "node_modules/@storybook/addon-backgrounds/node_modules/@storybook/theming": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.4.3.tgz", - "integrity": "sha512-u5wLwWmhGcTmkcs6f2wDGv+w8wzwbNJat0WaIIbwdJfX7arH6nO5HkBhNxvl6FUFxX0tovp/e9ULzxVPc356jw==", + "node_modules/@storybook/addon-backgrounds/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", "dev": true, "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.4.3", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/addon-controls": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.4.3.tgz", - "integrity": "sha512-wlfr0Yx27GzQqb5iINQTwL8wCW1NK8+4bJ/HQe4SQOY1FpybOK59B421V6YyQ3tafDWU5MMKh2sElMY9z5Deqw==", + "node_modules/@storybook/addon-backgrounds/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", "dev": true, "dependencies": { - "@storybook/blocks": "7.4.3", - "@storybook/client-logger": "7.4.3", - "@storybook/components": "7.4.3", - "@storybook/core-common": "7.4.3", - "@storybook/core-events": "7.4.3", - "@storybook/manager-api": "7.4.3", - "@storybook/node-logger": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/theming": "7.4.3", - "@storybook/types": "7.4.3", - "lodash": "^4.17.21", - "ts-dedent": "^2.0.0" + "@storybook/global": "^5.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } } }, - "node_modules/@storybook/addon-controls/node_modules/@storybook/blocks": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.4.3.tgz", - "integrity": "sha512-uyZVx3er1qOPFpKJtsbozBwt1Os3zqiq+2se7xDBK6ERr07zaRHLgRci7+kI8T5mdlCxYiGV+kzx5Vx5/7XaXg==", + "node_modules/@storybook/addon-backgrounds/node_modules/@storybook/components": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.4.6.tgz", + "integrity": "sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==", "dev": true, "dependencies": { - "@storybook/channels": "7.4.3", - "@storybook/client-logger": "7.4.3", - "@storybook/components": "7.4.3", - "@storybook/core-events": "7.4.3", + "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-toolbar": "^1.0.4", + "@storybook/client-logger": "7.4.6", "@storybook/csf": "^0.1.0", - "@storybook/docs-tools": "7.4.3", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/theming": "7.4.3", - "@storybook/types": "7.4.3", - "@types/lodash": "^4.14.167", - "color-convert": "^2.0.1", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "markdown-to-jsx": "^7.1.8", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", "memoizerific": "^1.11.3", - "polished": "^4.2.2", - "react-colorful": "^5.1.2", - "telejson": "^7.2.0", - "tocbot": "^4.20.1", - "ts-dedent": "^2.0.0", + "use-resize-observer": "^9.1.0", "util-deprecate": "^1.0.2" }, "funding": { @@ -4821,50 +4783,39 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/addon-controls/node_modules/@storybook/theming": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.4.3.tgz", - "integrity": "sha512-u5wLwWmhGcTmkcs6f2wDGv+w8wzwbNJat0WaIIbwdJfX7arH6nO5HkBhNxvl6FUFxX0tovp/e9ULzxVPc356jw==", + "node_modules/@storybook/addon-backgrounds/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", "dev": true, "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.4.3", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" + "ts-dedent": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/addon-docs": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.4.3.tgz", - "integrity": "sha512-c6r1nJY4fj/Uj9p7jHdicAS7quiK9RY0LJw+aB++FvcO1KavX33BlD2mxPIVU8a9oLJ3X4RUfNQz+OSABGy0xw==", + "node_modules/@storybook/addon-backgrounds/node_modules/@storybook/manager-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.4.6.tgz", + "integrity": "sha512-inrm3DIbCp8wjXSN/wK6e6i2ysQ/IEmtC7IN0OJ7vdrp+USCooPT448SQTUmVctUGCFmOU3fxXByq8g77oIi7w==", "dev": true, "dependencies": { - "@jest/transform": "^29.3.1", - "@mdx-js/react": "^2.1.5", - "@storybook/blocks": "7.4.3", - "@storybook/client-logger": "7.4.3", - "@storybook/components": "7.4.3", - "@storybook/csf-plugin": "7.4.3", - "@storybook/csf-tools": "7.4.3", + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/mdx2-csf": "^1.0.0", - "@storybook/node-logger": "7.4.3", - "@storybook/postinstall": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/react-dom-shim": "7.4.3", - "@storybook/theming": "7.4.3", - "@storybook/types": "7.4.3", - "fs-extra": "^11.1.0", - "remark-external-links": "^8.0.0", - "remark-slug": "^6.0.0", + "@storybook/router": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "semver": "^7.3.7", + "store2": "^2.14.2", + "telejson": "^7.2.0", "ts-dedent": "^2.0.0" }, "funding": { @@ -4876,85 +4827,41 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/blocks": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.4.3.tgz", - "integrity": "sha512-uyZVx3er1qOPFpKJtsbozBwt1Os3zqiq+2se7xDBK6ERr07zaRHLgRci7+kI8T5mdlCxYiGV+kzx5Vx5/7XaXg==", + "node_modules/@storybook/addon-backgrounds/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", "dev": true, "dependencies": { - "@storybook/channels": "7.4.3", - "@storybook/client-logger": "7.4.3", - "@storybook/components": "7.4.3", - "@storybook/core-events": "7.4.3", + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/csf": "^0.1.0", - "@storybook/docs-tools": "7.4.3", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/theming": "7.4.3", - "@storybook/types": "7.4.3", - "@types/lodash": "^4.14.167", - "color-convert": "^2.0.1", + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", - "markdown-to-jsx": "^7.1.8", "memoizerific": "^1.11.3", - "polished": "^4.2.2", - "react-colorful": "^5.1.2", - "telejson": "^7.2.0", - "tocbot": "^4.20.1", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", "ts-dedent": "^2.0.0", "util-deprecate": "^1.0.2" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/theming": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.4.3.tgz", - "integrity": "sha512-u5wLwWmhGcTmkcs6f2wDGv+w8wzwbNJat0WaIIbwdJfX7arH6nO5HkBhNxvl6FUFxX0tovp/e9ULzxVPc356jw==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.4.3", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/addon-essentials": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.4.3.tgz", - "integrity": "sha512-LYAauAz4YGWmdZw6umJisl3X0gk1UV9Ovm6b7hicNfKKYGlsWz9KNyi3kvV+harScBzcqENFl5kwezFu2Ltq9g==", + "node_modules/@storybook/addon-backgrounds/node_modules/@storybook/router": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.4.6.tgz", + "integrity": "sha512-Vl1esrHkcHxDKqc+HY7+6JQpBPW3zYvGk0cQ2rxVMhWdLZTAz1hss9DqzN9tFnPyfn0a1Q77EpMySkUrvWKKNQ==", "dev": true, "dependencies": { - "@storybook/addon-actions": "7.4.3", - "@storybook/addon-backgrounds": "7.4.3", - "@storybook/addon-controls": "7.4.3", - "@storybook/addon-docs": "7.4.3", - "@storybook/addon-highlight": "7.4.3", - "@storybook/addon-measure": "7.4.3", - "@storybook/addon-outline": "7.4.3", - "@storybook/addon-toolbars": "7.4.3", - "@storybook/addon-viewport": "7.4.3", - "@storybook/core-common": "7.4.3", - "@storybook/manager-api": "7.4.3", - "@storybook/node-logger": "7.4.3", - "@storybook/preview-api": "7.4.3", - "ts-dedent": "^2.0.0" + "@storybook/client-logger": "7.4.6", + "memoizerific": "^1.11.3", + "qs": "^6.10.0" }, "funding": { "type": "opencollective", @@ -4965,40 +4872,40 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/addon-highlight": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.4.3.tgz", - "integrity": "sha512-4FDvg+ZH5/H6b7qI6tVSygCaF5h7TStfyLXwxx07edot0vaaw4ir/0sbCAH9AUQ9/+08RiXsMFO5tgMUp/BjcA==", + "node_modules/@storybook/addon-backgrounds/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", "dev": true, "dependencies": { - "@storybook/core-events": "7.4.3", - "@storybook/global": "^5.0.0", - "@storybook/preview-api": "7.4.3" + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/addon-interactions": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-7.4.3.tgz", - "integrity": "sha512-72Uy7FGr3UbEq44D44ML/o/kC8jUuBETDgnNTC/J7n35OzHcBcas9cHzam87IG/M8uxTwKtuUlEzwyoNUjI3MA==", + "node_modules/@storybook/addon-controls": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.4.6.tgz", + "integrity": "sha512-4lq3sycEUIsK8SUWDYc60QgF4vV9FZZ3lDr6M7j2W9bOnvGw49d2fbdlnq+bX1ZprZZ9VgglQpBAorQB3BXZRw==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.3", - "@storybook/components": "7.4.3", - "@storybook/core-common": "7.4.3", - "@storybook/core-events": "7.4.3", - "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "7.4.3", - "@storybook/manager-api": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/theming": "7.4.3", - "@storybook/types": "7.4.3", - "jest-mock": "^27.0.6", - "polished": "^4.2.2", - "ts-dedent": "^2.2.0" + "@storybook/blocks": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/components": "7.4.6", + "@storybook/core-common": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/manager-api": "7.4.6", + "@storybook/node-logger": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "lodash": "^4.17.21", + "ts-dedent": "^2.0.0" }, "funding": { "type": "opencollective", @@ -5017,32 +4924,1367 @@ } } }, - "node_modules/@storybook/addon-interactions/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "node_modules/@storybook/addon-controls/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", "dev": true, "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/addon-interactions/node_modules/@storybook/theming": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.4.3.tgz", - "integrity": "sha512-u5wLwWmhGcTmkcs6f2wDGv+w8wzwbNJat0WaIIbwdJfX7arH6nO5HkBhNxvl6FUFxX0tovp/e9ULzxVPc356jw==", + "node_modules/@storybook/addon-controls/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-controls/node_modules/@storybook/components": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.4.6.tgz", + "integrity": "sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==", + "dev": true, + "dependencies": { + "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-toolbar": "^1.0.4", + "@storybook/client-logger": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "memoizerific": "^1.11.3", + "use-resize-observer": "^9.1.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-controls/node_modules/@storybook/core-common": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.4.6.tgz", + "integrity": "sha512-05MJFmOM86qvTLtgDskokIFz9txe0Lbhq4L3by1FtF0GwgH+p+W6I94KI7c6ANER+kVZkXQZhiRzwBFnVTW+Cg==", + "dev": true, + "dependencies": { + "@storybook/core-events": "7.4.6", + "@storybook/node-logger": "7.4.6", + "@storybook/types": "7.4.6", + "@types/find-cache-dir": "^3.2.1", + "@types/node": "^16.0.0", + "@types/node-fetch": "^2.6.4", + "@types/pretty-hrtime": "^1.0.0", + "chalk": "^4.1.0", + "esbuild": "^0.18.0", + "esbuild-register": "^3.4.0", + "file-system-cache": "2.3.0", + "find-cache-dir": "^3.0.0", + "find-up": "^5.0.0", + "fs-extra": "^11.1.0", + "glob": "^10.0.0", + "handlebars": "^4.7.7", + "lazy-universal-dotenv": "^4.0.0", + "node-fetch": "^2.0.0", + "picomatch": "^2.3.0", + "pkg-dir": "^5.0.0", + "pretty-hrtime": "^1.0.3", + "resolve-from": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-controls/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-controls/node_modules/@storybook/manager-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.4.6.tgz", + "integrity": "sha512-inrm3DIbCp8wjXSN/wK6e6i2ysQ/IEmtC7IN0OJ7vdrp+USCooPT448SQTUmVctUGCFmOU3fxXByq8g77oIi7w==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/router": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "semver": "^7.3.7", + "store2": "^2.14.2", + "telejson": "^7.2.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-controls/node_modules/@storybook/node-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.4.6.tgz", + "integrity": "sha512-djZb310Q27GviDug1XBv0jOEDLCiwr4hhDE0aifCEKZpfNCi/EaP31nbWimFzZwxu4hE/YAPWExzScruR1zw9Q==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-controls/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-controls/node_modules/@storybook/router": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.4.6.tgz", + "integrity": "sha512-Vl1esrHkcHxDKqc+HY7+6JQpBPW3zYvGk0cQ2rxVMhWdLZTAz1hss9DqzN9tFnPyfn0a1Q77EpMySkUrvWKKNQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "memoizerific": "^1.11.3", + "qs": "^6.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-controls/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-controls/node_modules/@types/node": { + "version": "16.18.58", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.58.tgz", + "integrity": "sha512-YGncyA25/MaVtQkjWW9r0EFBukZ+JulsLcVZBlGUfIb96OBMjkoRWwQo5IEWJ8Fj06Go3GHw+bjYDitv6BaGsA==", + "dev": true + }, + "node_modules/@storybook/addon-docs": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.4.6.tgz", + "integrity": "sha512-dLaub+XWFq4hChw+xfuF9yYg0Txp77FUawKoAigccfjWXx+OOhRV3XTuAcknpXkYq94GWynHgUFXosXT9kbDNA==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.3.1", + "@mdx-js/react": "^2.1.5", + "@storybook/blocks": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/components": "7.4.6", + "@storybook/csf-plugin": "7.4.6", + "@storybook/csf-tools": "7.4.6", + "@storybook/global": "^5.0.0", + "@storybook/mdx2-csf": "^1.0.0", + "@storybook/node-logger": "7.4.6", + "@storybook/postinstall": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/react-dom-shim": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "fs-extra": "^11.1.0", + "remark-external-links": "^8.0.0", + "remark-slug": "^6.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-docs/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-docs/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-docs/node_modules/@storybook/components": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.4.6.tgz", + "integrity": "sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==", + "dev": true, + "dependencies": { + "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-toolbar": "^1.0.4", + "@storybook/client-logger": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "memoizerific": "^1.11.3", + "use-resize-observer": "^9.1.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-docs/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-docs/node_modules/@storybook/node-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.4.6.tgz", + "integrity": "sha512-djZb310Q27GviDug1XBv0jOEDLCiwr4hhDE0aifCEKZpfNCi/EaP31nbWimFzZwxu4hE/YAPWExzScruR1zw9Q==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-docs/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-docs/node_modules/@storybook/react-dom-shim": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.4.6.tgz", + "integrity": "sha512-DSq8l9FDocUF1ooVI+TF83pddj1LynE/Hv0/y8XZhc3IgJ/HkuOQuUmfz29ezgfAi9gFYUR8raTIBi3/xdoRmw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-docs/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-essentials": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.4.6.tgz", + "integrity": "sha512-dWodufrt71TK7ELkeIvVae/x4PzECUlbOm57Iqqt4yQCyR291CgvI4PjeB8un2HbpcXCGZ+N/Oj3YkytvzBi4A==", + "dev": true, + "dependencies": { + "@storybook/addon-actions": "7.4.6", + "@storybook/addon-backgrounds": "7.4.6", + "@storybook/addon-controls": "7.4.6", + "@storybook/addon-docs": "7.4.6", + "@storybook/addon-highlight": "7.4.6", + "@storybook/addon-measure": "7.4.6", + "@storybook/addon-outline": "7.4.6", + "@storybook/addon-toolbars": "7.4.6", + "@storybook/addon-viewport": "7.4.6", + "@storybook/core-common": "7.4.6", + "@storybook/manager-api": "7.4.6", + "@storybook/node-logger": "7.4.6", + "@storybook/preview-api": "7.4.6", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/addon-actions": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.4.6.tgz", + "integrity": "sha512-SsqZr3js5NinKPnC8AeNI7Ij+Q6fIl9tRdRmSulEgjksjOg7E5S1/Wsn5Bb2CCgj7MaX6VxGyC7s3XskQtDiIQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/components": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "@storybook/manager-api": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "polished": "^4.2.2", + "prop-types": "^15.7.2", + "react-inspector": "^6.0.0", + "telejson": "^7.2.0", + "ts-dedent": "^2.0.0", + "uuid": "^9.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/components": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.4.6.tgz", + "integrity": "sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==", + "dev": true, + "dependencies": { + "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-toolbar": "^1.0.4", + "@storybook/client-logger": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "memoizerific": "^1.11.3", + "use-resize-observer": "^9.1.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/core-common": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.4.6.tgz", + "integrity": "sha512-05MJFmOM86qvTLtgDskokIFz9txe0Lbhq4L3by1FtF0GwgH+p+W6I94KI7c6ANER+kVZkXQZhiRzwBFnVTW+Cg==", + "dev": true, + "dependencies": { + "@storybook/core-events": "7.4.6", + "@storybook/node-logger": "7.4.6", + "@storybook/types": "7.4.6", + "@types/find-cache-dir": "^3.2.1", + "@types/node": "^16.0.0", + "@types/node-fetch": "^2.6.4", + "@types/pretty-hrtime": "^1.0.0", + "chalk": "^4.1.0", + "esbuild": "^0.18.0", + "esbuild-register": "^3.4.0", + "file-system-cache": "2.3.0", + "find-cache-dir": "^3.0.0", + "find-up": "^5.0.0", + "fs-extra": "^11.1.0", + "glob": "^10.0.0", + "handlebars": "^4.7.7", + "lazy-universal-dotenv": "^4.0.0", + "node-fetch": "^2.0.0", + "picomatch": "^2.3.0", + "pkg-dir": "^5.0.0", + "pretty-hrtime": "^1.0.3", + "resolve-from": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/manager-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.4.6.tgz", + "integrity": "sha512-inrm3DIbCp8wjXSN/wK6e6i2ysQ/IEmtC7IN0OJ7vdrp+USCooPT448SQTUmVctUGCFmOU3fxXByq8g77oIi7w==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/router": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "semver": "^7.3.7", + "store2": "^2.14.2", + "telejson": "^7.2.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/node-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.4.6.tgz", + "integrity": "sha512-djZb310Q27GviDug1XBv0jOEDLCiwr4hhDE0aifCEKZpfNCi/EaP31nbWimFzZwxu4hE/YAPWExzScruR1zw9Q==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/router": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.4.6.tgz", + "integrity": "sha512-Vl1esrHkcHxDKqc+HY7+6JQpBPW3zYvGk0cQ2rxVMhWdLZTAz1hss9DqzN9tFnPyfn0a1Q77EpMySkUrvWKKNQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "memoizerific": "^1.11.3", + "qs": "^6.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-essentials/node_modules/@types/node": { + "version": "16.18.58", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.58.tgz", + "integrity": "sha512-YGncyA25/MaVtQkjWW9r0EFBukZ+JulsLcVZBlGUfIb96OBMjkoRWwQo5IEWJ8Fj06Go3GHw+bjYDitv6BaGsA==", + "dev": true + }, + "node_modules/@storybook/addon-highlight": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.4.6.tgz", + "integrity": "sha512-zCufxxD2KS5VwczxfkcBxe1oR/juTTn2H1Qm8kYvWCJQx3UxzX0+G9cwafbpV7eivqaufLweEwROkH+0KjAtkQ==", + "dev": true, + "dependencies": { + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "@storybook/preview-api": "7.4.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-highlight/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-highlight/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-highlight/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-highlight/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-highlight/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-interactions": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-7.4.3.tgz", + "integrity": "sha512-72Uy7FGr3UbEq44D44ML/o/kC8jUuBETDgnNTC/J7n35OzHcBcas9cHzam87IG/M8uxTwKtuUlEzwyoNUjI3MA==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.3", + "@storybook/components": "7.4.3", + "@storybook/core-common": "7.4.3", + "@storybook/core-events": "7.4.3", + "@storybook/global": "^5.0.0", + "@storybook/instrumenter": "7.4.3", + "@storybook/manager-api": "7.4.3", + "@storybook/preview-api": "7.4.3", + "@storybook/theming": "7.4.3", + "@storybook/types": "7.4.3", + "jest-mock": "^27.0.6", + "polished": "^4.2.2", + "ts-dedent": "^2.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/addon-interactions/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@storybook/addon-interactions/node_modules/@storybook/theming": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.4.3.tgz", + "integrity": "sha512-u5wLwWmhGcTmkcs6f2wDGv+w8wzwbNJat0WaIIbwdJfX7arH6nO5HkBhNxvl6FUFxX0tovp/e9ULzxVPc356jw==", + "dev": true, + "dependencies": { + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", + "@storybook/client-logger": "7.4.3", + "@storybook/global": "^5.0.0", + "memoizerific": "^1.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-interactions/node_modules/@types/yargs": { + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@storybook/addon-interactions/node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@storybook/addon-links": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.4.3.tgz", + "integrity": "sha512-flnwlKdePQtwgryFhJlju94DVvZBq477xaD1mG9zcqEe+QeN+1GGggIo6R9e2hEsWcAfpc2yKA4dJP9KS9xIHg==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.3", + "@storybook/core-events": "7.4.3", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/manager-api": "7.4.3", + "@storybook/preview-api": "7.4.3", + "@storybook/router": "7.4.3", + "@storybook/types": "7.4.3", + "prop-types": "^15.7.2", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/addon-measure": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.4.6.tgz", + "integrity": "sha512-nCymMLaHnxv8TE3yEM1A9Tulb1NuRXRNmtsdHTkjv7P1aWCxZo8A/GZaottKe/GLT8jSRjZ+dnpYWrbAhw6wTQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/components": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "@storybook/manager-api": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/types": "7.4.6", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/addon-measure/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-measure/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-measure/node_modules/@storybook/components": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.4.6.tgz", + "integrity": "sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==", + "dev": true, + "dependencies": { + "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-toolbar": "^1.0.4", + "@storybook/client-logger": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "memoizerific": "^1.11.3", + "use-resize-observer": "^9.1.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-measure/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-measure/node_modules/@storybook/manager-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.4.6.tgz", + "integrity": "sha512-inrm3DIbCp8wjXSN/wK6e6i2ysQ/IEmtC7IN0OJ7vdrp+USCooPT448SQTUmVctUGCFmOU3fxXByq8g77oIi7w==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/router": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "semver": "^7.3.7", + "store2": "^2.14.2", + "telejson": "^7.2.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-measure/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-measure/node_modules/@storybook/router": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.4.6.tgz", + "integrity": "sha512-Vl1esrHkcHxDKqc+HY7+6JQpBPW3zYvGk0cQ2rxVMhWdLZTAz1hss9DqzN9tFnPyfn0a1Q77EpMySkUrvWKKNQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "memoizerific": "^1.11.3", + "qs": "^6.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-measure/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-outline": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.4.6.tgz", + "integrity": "sha512-errNUblRVDLpuEaHQPr/nsrnsUkD2ARmXawkRvizgDWLIDMDJYjTON3MUCaVx3x+hlZ3I6X//G5TVcma8tCc8A==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/components": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "@storybook/manager-api": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/types": "7.4.6", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/addon-outline/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-outline/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-outline/node_modules/@storybook/components": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.4.6.tgz", + "integrity": "sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==", + "dev": true, + "dependencies": { + "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-toolbar": "^1.0.4", + "@storybook/client-logger": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "memoizerific": "^1.11.3", + "use-resize-observer": "^9.1.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-outline/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-outline/node_modules/@storybook/manager-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.4.6.tgz", + "integrity": "sha512-inrm3DIbCp8wjXSN/wK6e6i2ysQ/IEmtC7IN0OJ7vdrp+USCooPT448SQTUmVctUGCFmOU3fxXByq8g77oIi7w==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/router": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "semver": "^7.3.7", + "store2": "^2.14.2", + "telejson": "^7.2.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-outline/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-outline/node_modules/@storybook/router": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.4.6.tgz", + "integrity": "sha512-Vl1esrHkcHxDKqc+HY7+6JQpBPW3zYvGk0cQ2rxVMhWdLZTAz1hss9DqzN9tFnPyfn0a1Q77EpMySkUrvWKKNQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "memoizerific": "^1.11.3", + "qs": "^6.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-outline/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-toolbars": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.4.6.tgz", + "integrity": "sha512-L9m2FBcKeteGq7qIYsMJr0LEfiH7Wdrv5IDcldZTn68eZUJTh1p4GdJZcOmzX1P5IFRr76hpu03iWsNlWQjpbQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/components": "7.4.6", + "@storybook/manager-api": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/theming": "7.4.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/addon-toolbars/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", "dev": true, "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.4.3", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-toolbars/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-toolbars/node_modules/@storybook/components": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.4.6.tgz", + "integrity": "sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==", + "dev": true, + "dependencies": { + "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-toolbar": "^1.0.4", + "@storybook/client-logger": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "memoizerific": "^1.11.3", + "use-resize-observer": "^9.1.0", + "util-deprecate": "^1.0.2" }, "funding": { "type": "opencollective", @@ -5053,44 +6295,85 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/addon-interactions/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "node_modules/@storybook/addon-toolbars/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", "dev": true, "dependencies": { - "@types/yargs-parser": "*" + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/addon-interactions/node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "node_modules/@storybook/addon-toolbars/node_modules/@storybook/manager-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.4.6.tgz", + "integrity": "sha512-inrm3DIbCp8wjXSN/wK6e6i2ysQ/IEmtC7IN0OJ7vdrp+USCooPT448SQTUmVctUGCFmOU3fxXByq8g77oIi7w==", "dev": true, "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/router": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "semver": "^7.3.7", + "store2": "^2.14.2", + "telejson": "^7.2.0", + "ts-dedent": "^2.0.0" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/addon-links": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.4.3.tgz", - "integrity": "sha512-flnwlKdePQtwgryFhJlju94DVvZBq477xaD1mG9zcqEe+QeN+1GGggIo6R9e2hEsWcAfpc2yKA4dJP9KS9xIHg==", + "node_modules/@storybook/addon-toolbars/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.3", - "@storybook/core-events": "7.4.3", + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/csf": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/router": "7.4.3", - "@storybook/types": "7.4.3", - "prop-types": "^15.7.2", - "ts-dedent": "^2.0.0" + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-toolbars/node_modules/@storybook/router": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.4.6.tgz", + "integrity": "sha512-Vl1esrHkcHxDKqc+HY7+6JQpBPW3zYvGk0cQ2rxVMhWdLZTAz1hss9DqzN9tFnPyfn0a1Q77EpMySkUrvWKKNQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "memoizerific": "^1.11.3", + "qs": "^6.10.0" }, "funding": { "type": "opencollective", @@ -5099,30 +6382,39 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-toolbars/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/addon-measure": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.4.3.tgz", - "integrity": "sha512-a07/GV9WWvqy1MuJtDevHzPo/weY86s7JT+qjGk0bhQdThVcd94Z7whlQL/LgrdAi1XLdHY5R5LpUIk9UDluNw==", + "node_modules/@storybook/addon-viewport": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.4.6.tgz", + "integrity": "sha512-INDtk54j7bi7NgxMfd2ATmbA0J7nAd6X8itMkLIyPuPJtx8bYHPDORyemDOd0AojgmAdTOAyUtDYdI/PFeo4Cw==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.3", - "@storybook/components": "7.4.3", - "@storybook/core-events": "7.4.3", + "@storybook/client-logger": "7.4.6", + "@storybook/components": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/types": "7.4.3", - "tiny-invariant": "^1.3.1" + "@storybook/manager-api": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/theming": "7.4.6", + "memoizerific": "^1.11.3", + "prop-types": "^15.7.2" }, "funding": { "type": "opencollective", @@ -5141,20 +6433,53 @@ } } }, - "node_modules/@storybook/addon-outline": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.4.3.tgz", - "integrity": "sha512-QPcTjmNgj0+7NEzomfqNOnm2DgcRjqvYGCdlxfDbnNB0J+ZGlaUozL3ZbofJKx9qCoHf+j+Z1pwONHafJV6t4w==", + "node_modules/@storybook/addon-viewport/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.3", - "@storybook/components": "7.4.3", - "@storybook/core-events": "7.4.3", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/types": "7.4.3", - "ts-dedent": "^2.0.0" + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-viewport/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-viewport/node_modules/@storybook/components": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.4.6.tgz", + "integrity": "sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==", + "dev": true, + "dependencies": { + "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-toolbar": "^1.0.4", + "@storybook/client-logger": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "memoizerific": "^1.11.3", + "use-resize-observer": "^9.1.0", + "util-deprecate": "^1.0.2" }, "funding": { "type": "opencollective", @@ -5163,27 +6488,42 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-viewport/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/addon-toolbars": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.4.3.tgz", - "integrity": "sha512-sHILofAarfzku+8qhueELoZYCLTHuDtmnlfILjBrH/w7Et3Vnyn1wJcdal7VnQPbX9EiEkdFaiZybQdniBb+hQ==", + "node_modules/@storybook/addon-viewport/node_modules/@storybook/manager-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.4.6.tgz", + "integrity": "sha512-inrm3DIbCp8wjXSN/wK6e6i2ysQ/IEmtC7IN0OJ7vdrp+USCooPT448SQTUmVctUGCFmOU3fxXByq8g77oIi7w==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.3", - "@storybook/components": "7.4.3", - "@storybook/manager-api": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/theming": "7.4.3" + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", + "@storybook/global": "^5.0.0", + "@storybook/router": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "semver": "^7.3.7", + "store2": "^2.14.2", + "telejson": "^7.2.0", + "ts-dedent": "^2.0.0" }, "funding": { "type": "opencollective", @@ -5192,51 +6532,43 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } } }, - "node_modules/@storybook/addon-toolbars/node_modules/@storybook/theming": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.4.3.tgz", - "integrity": "sha512-u5wLwWmhGcTmkcs6f2wDGv+w8wzwbNJat0WaIIbwdJfX7arH6nO5HkBhNxvl6FUFxX0tovp/e9ULzxVPc356jw==", + "node_modules/@storybook/addon-viewport/node_modules/@storybook/preview-api": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", "dev": true, "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.4.3", + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/csf": "^0.1.0", "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" + "@storybook/types": "7.4.6", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/addon-viewport": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.4.3.tgz", - "integrity": "sha512-jDRG6ZMZ4ATOXiJQcXTpolTtIi8oAhbk6mbJyj65nClXgWqfZxMK9PMfJw5R7zHhAmrKoWNTDc72eayFOIHaNg==", + "node_modules/@storybook/addon-viewport/node_modules/@storybook/router": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.4.6.tgz", + "integrity": "sha512-Vl1esrHkcHxDKqc+HY7+6JQpBPW3zYvGk0cQ2rxVMhWdLZTAz1hss9DqzN9tFnPyfn0a1Q77EpMySkUrvWKKNQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.3", - "@storybook/components": "7.4.3", - "@storybook/core-events": "7.4.3", - "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/theming": "7.4.3", + "@storybook/client-logger": "7.4.6", "memoizerific": "^1.11.3", - "prop-types": "^15.7.2" + "qs": "^6.10.0" }, "funding": { "type": "opencollective", @@ -5245,34 +6577,22 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } } }, - "node_modules/@storybook/addon-viewport/node_modules/@storybook/theming": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.4.3.tgz", - "integrity": "sha512-u5wLwWmhGcTmkcs6f2wDGv+w8wzwbNJat0WaIIbwdJfX7arH6nO5HkBhNxvl6FUFxX0tovp/e9ULzxVPc356jw==", + "node_modules/@storybook/addon-viewport/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", "dev": true, "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.4.3", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/@storybook/addons": { @@ -5295,22 +6615,22 @@ } }, "node_modules/@storybook/blocks": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.4.5.tgz", - "integrity": "sha512-FhAIkCT2HrzJcKsC3mL5+uG3GrbS23mYAT1h3iyPjCliZzxfCCI9UCMUXqYx4Z/FmAGJgpsQQXiBFZuoTHO9aQ==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.4.6.tgz", + "integrity": "sha512-HxBSAeOiTZW2jbHQlo1upRWFgoMsaAyKijUFf5MwwMNIesXCuuTGZDJ3xTABwAVLK2qC9Ektfbo0CZCiPVuDRQ==", "dev": true, "dependencies": { - "@storybook/channels": "7.4.5", - "@storybook/client-logger": "7.4.5", - "@storybook/components": "7.4.5", - "@storybook/core-events": "7.4.5", + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/components": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/csf": "^0.1.0", - "@storybook/docs-tools": "7.4.5", + "@storybook/docs-tools": "7.4.6", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.4.5", - "@storybook/preview-api": "7.4.5", - "@storybook/theming": "7.4.5", - "@storybook/types": "7.4.5", + "@storybook/manager-api": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", "@types/lodash": "^4.14.167", "color-convert": "^2.0.1", "dequal": "^2.0.2", @@ -5334,13 +6654,13 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/channels": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.5.tgz", - "integrity": "sha512-zWPZn4CxPFXsrrSRQ9JD8GmTeWeFYgr3sTBpe23hnhYookCXVNJ6AcaXogrT9b2ALfbB6MiFDbZIHHTgIgbWpg==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.5", - "@storybook/core-events": "7.4.5", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/global": "^5.0.0", "qs": "^6.10.0", "telejson": "^7.2.0", @@ -5352,9 +6672,9 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/client-logger": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.5.tgz", - "integrity": "sha512-Bn6eTAjhPDUfLpvuxhKkpDpOtkadfkSmkBNBZRu3r0Dzk2J1nNyKV5K6D8dOU4PFVof4z/gXYj5bktT29jKsmw==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" @@ -5365,18 +6685,18 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/components": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.4.5.tgz", - "integrity": "sha512-boskkfvMBB8CFYY9+1ofFNyKrdWXTY/ghzt7oK80dz6f2Eseo/WXK3OsCdCq5vWbLRCdbgJ8zXG8pAFi4yBsxA==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.4.6.tgz", + "integrity": "sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==", "dev": true, "dependencies": { "@radix-ui/react-select": "^1.2.2", "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.4.5", + "@storybook/client-logger": "7.4.6", "@storybook/csf": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/theming": "7.4.5", - "@storybook/types": "7.4.5", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", "memoizerific": "^1.11.3", "use-resize-observer": "^9.1.0", "util-deprecate": "^1.0.2" @@ -5391,14 +6711,14 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/core-common": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.4.5.tgz", - "integrity": "sha512-c4pBuILMD4YhSpJ+QpKtsUZpK+/rfolwOvzXfJwlN5EpYzMz6FjVR/LyX0cCT2YLI3X5YWRoCdvMxy5Aeryb8g==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.4.6.tgz", + "integrity": "sha512-05MJFmOM86qvTLtgDskokIFz9txe0Lbhq4L3by1FtF0GwgH+p+W6I94KI7c6ANER+kVZkXQZhiRzwBFnVTW+Cg==", "dev": true, "dependencies": { - "@storybook/core-events": "7.4.5", - "@storybook/node-logger": "7.4.5", - "@storybook/types": "7.4.5", + "@storybook/core-events": "7.4.6", + "@storybook/node-logger": "7.4.6", + "@storybook/types": "7.4.6", "@types/find-cache-dir": "^3.2.1", "@types/node": "^16.0.0", "@types/node-fetch": "^2.6.4", @@ -5426,9 +6746,9 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/core-events": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.5.tgz", - "integrity": "sha512-Jzy/adSC95saYCZlgXE5j7jmiMLAXYpnBFBxEtBdXwSWEBb0zt21n1nyWBEAv9s/k2gqDXlPHKHeL5Mn6y40zA==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", "dev": true, "dependencies": { "ts-dedent": "^2.0.0" @@ -5439,14 +6759,14 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/docs-tools": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.4.5.tgz", - "integrity": "sha512-ctK+yGb2nvWISSvCCzj3ZhDaAb7I2BLjbxuBGTyNPvl4V9UQ9LBYzdJwR50q+DfscxdwSHMSOE/0OnzmJdaSJA==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.4.6.tgz", + "integrity": "sha512-nZj1L/8WwKWWJ41FW4MaKGajZUtrhnr9UwflRCkQJaWhAKmDfOb5M5TqI93uCOULpFPOm5wpoMBz2IHInQ2Lrg==", "dev": true, "dependencies": { - "@storybook/core-common": "7.4.5", - "@storybook/preview-api": "7.4.5", - "@storybook/types": "7.4.5", + "@storybook/core-common": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/types": "7.4.6", "@types/doctrine": "^0.0.3", "doctrine": "^3.0.0", "lodash": "^4.17.21" @@ -5457,19 +6777,19 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/manager-api": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.4.5.tgz", - "integrity": "sha512-8Hdh5Tutet8xRy2fAknczfvpshz09eVnLd8m34vcFceUOYvEnvDbWerufhlEzovsF4v7U32uqbDHKdKTamWEQQ==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.4.6.tgz", + "integrity": "sha512-inrm3DIbCp8wjXSN/wK6e6i2ysQ/IEmtC7IN0OJ7vdrp+USCooPT448SQTUmVctUGCFmOU3fxXByq8g77oIi7w==", "dev": true, "dependencies": { - "@storybook/channels": "7.4.5", - "@storybook/client-logger": "7.4.5", - "@storybook/core-events": "7.4.5", + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/csf": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/router": "7.4.5", - "@storybook/theming": "7.4.5", - "@storybook/types": "7.4.5", + "@storybook/router": "7.4.6", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", "dequal": "^2.0.2", "lodash": "^4.17.21", "memoizerific": "^1.11.3", @@ -5488,9 +6808,9 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/node-logger": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.4.5.tgz", - "integrity": "sha512-fJSykphbryuEYj1qihbaTH5oOzD4NkptRxyf2uyBrpgkr5tCTq9d7GHheqaBuIdi513dsjlcIR7z5iHxW7ZD+Q==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.4.6.tgz", + "integrity": "sha512-djZb310Q27GviDug1XBv0jOEDLCiwr4hhDE0aifCEKZpfNCi/EaP31nbWimFzZwxu4hE/YAPWExzScruR1zw9Q==", "dev": true, "funding": { "type": "opencollective", @@ -5498,17 +6818,17 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/preview-api": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.5.tgz", - "integrity": "sha512-6xXQZPyilkGVddfZBI7tMbMMgOyIoZTYgTnwSPTMsXxO0f0TvtNDmGdwhn0I1nREHKfiQGpcQe6gwddEMnGtSg==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.4.6.tgz", + "integrity": "sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==", "dev": true, "dependencies": { - "@storybook/channels": "7.4.5", - "@storybook/client-logger": "7.4.5", - "@storybook/core-events": "7.4.5", + "@storybook/channels": "7.4.6", + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", "@storybook/csf": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/types": "7.4.5", + "@storybook/types": "7.4.6", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -5524,12 +6844,12 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/router": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.4.5.tgz", - "integrity": "sha512-IM4IhiPiXsx3FAUeUOAB47uiuUS8Yd37VQcNlXLBO28GgHoTSYOrjS+VTGLIV5cAGKr8+H5pFB+q35BnlFUpkQ==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.4.6.tgz", + "integrity": "sha512-Vl1esrHkcHxDKqc+HY7+6JQpBPW3zYvGk0cQ2rxVMhWdLZTAz1hss9DqzN9tFnPyfn0a1Q77EpMySkUrvWKKNQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.4.5", + "@storybook/client-logger": "7.4.6", "memoizerific": "^1.11.3", "qs": "^6.10.0" }, @@ -5543,12 +6863,12 @@ } }, "node_modules/@storybook/blocks/node_modules/@storybook/types": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.5.tgz", - "integrity": "sha512-DTWFNjfRTpncjufDoUs0QnNkgHG2qThGKWL1D6sO18cYI02zWPyHWD8/cbqlvtT7XIGe3s1iUEfCTdU5GcwWBA==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", "dev": true, "dependencies": { - "@storybook/channels": "7.4.5", + "@storybook/channels": "7.4.6", "@types/babel__core": "^7.0.0", "@types/express": "^4.7.0", "file-system-cache": "2.3.0" @@ -5559,9 +6879,9 @@ } }, "node_modules/@storybook/blocks/node_modules/@types/node": { - "version": "16.18.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.54.tgz", - "integrity": "sha512-oTmGy68gxZZ21FhTJVVvZBYpQHEBZxHKTsGshobMqm9qWpbqdZsA5jvsuPZcHu0KwpmLrOHWPdEfg7XDpNT9UA==", + "version": "16.18.58", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.58.tgz", + "integrity": "sha512-YGncyA25/MaVtQkjWW9r0EFBukZ+JulsLcVZBlGUfIb96OBMjkoRWwQo5IEWJ8Fj06Go3GHw+bjYDitv6BaGsA==", "dev": true }, "node_modules/@storybook/builder-manager": { @@ -6599,12 +7919,12 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.4.3.tgz", - "integrity": "sha512-xQCimGsrGD1JxvyFc0LrH10WZWb181r0beF19aGIAadczs/JWhT+nxF8OhfP1LK4wHj9jH+F4nIXEMpm9yI9Qg==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.4.6.tgz", + "integrity": "sha512-yi7Qa4NSqKOyiJTWCxlB0ih2ijXq6oY5qZKW6MuMMBP14xJNRGLbH5KabpfXgN2T7YECcOWG1uWaGj2veJb1KA==", "dev": true, "dependencies": { - "@storybook/csf-tools": "7.4.3", + "@storybook/csf-tools": "7.4.6", "unplugin": "^1.3.1" }, "funding": { @@ -6613,9 +7933,9 @@ } }, "node_modules/@storybook/csf-tools": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.4.3.tgz", - "integrity": "sha512-nkVakGx2kzou91lGcxnyFNiSEdnpx1a53lQTl/DLm0QpDbqQuu3ZbZWXZCpXV97t/6YPeCCnGLXodnI7PZyZBA==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.4.6.tgz", + "integrity": "sha512-ocKpcIUtTBy6hlLY34RUFQyX403cWpB2gGfqvkHbpGe2BQj7EyV0zpWnjsfVxvw+M9OWlCdxHWDOPUgXM33ELw==", "dev": true, "dependencies": { "@babel/generator": "^7.22.9", @@ -6623,7 +7943,7 @@ "@babel/traverse": "^7.22.8", "@babel/types": "^7.22.5", "@storybook/csf": "^0.1.0", - "@storybook/types": "7.4.3", + "@storybook/types": "7.4.6", "fs-extra": "^11.1.0", "recast": "^0.23.1", "ts-dedent": "^2.0.0" @@ -6633,6 +7953,66 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/csf-tools/node_modules/@storybook/channels": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", + "integrity": "sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/csf-tools/node_modules/@storybook/client-logger": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/csf-tools/node_modules/@storybook/core-events": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.4.6.tgz", + "integrity": "sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/csf-tools/node_modules/@storybook/types": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.4.6.tgz", + "integrity": "sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "7.4.6", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, "node_modules/@storybook/docs-mdx": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@storybook/docs-mdx/-/docs-mdx-0.1.0.tgz", @@ -6898,9 +8278,9 @@ } }, "node_modules/@storybook/postinstall": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.4.3.tgz", - "integrity": "sha512-6NMaAvL4a26jR50UPz+Q6VATY3lHZWw1ru/weFgiV0rat632RFdiFyrMMrjbUWu9HDJE4fbCzrIZU0jGVs1wlQ==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.4.6.tgz", + "integrity": "sha512-TqI5BucPAGRWrkh55BYiG2/gHLFtC0In4cuu0GsUzB/1jc4i51npLRorCwhmT7r7YliGl5F7JaP0Bni/qHN3Lg==", "dev": true, "funding": { "type": "opencollective", @@ -7498,13 +8878,13 @@ } }, "node_modules/@storybook/theming": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.4.5.tgz", - "integrity": "sha512-QSIJDIMzOegzlhubIBaYIovf4mlf+AVL0SmQOskPS8GZ6s9t77yUUI6gZTEjO+S4eB3djXRsfTTijQ8+z4XmRA==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.4.6.tgz", + "integrity": "sha512-HW77iJ9ptCMqhoBOYFjRQw7VBap+38fkJGHP5KylEJCyYCgIAm2dEcQmtWpMVYFssSGcb6djfbtAMhYU4TL4Iw==", "dev": true, "dependencies": { "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.4.5", + "@storybook/client-logger": "7.4.6", "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3" }, @@ -7518,9 +8898,9 @@ } }, "node_modules/@storybook/theming/node_modules/@storybook/client-logger": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.5.tgz", - "integrity": "sha512-Bn6eTAjhPDUfLpvuxhKkpDpOtkadfkSmkBNBZRu3r0Dzk2J1nNyKV5K6D8dOU4PFVof4z/gXYj5bktT29jKsmw==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.4.6.tgz", + "integrity": "sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" @@ -8083,9 +9463,9 @@ "dev": true }, "node_modules/@types/mdx": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.7.tgz", - "integrity": "sha512-BG4tyr+4amr3WsSEmHn/fXPqaCba/AYZ7dsaQTiavihQunHSIxk+uAtqsjvicNpyHN6cm+B9RVrUOtW9VzIKHw==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.8.tgz", + "integrity": "sha512-r7/zWe+f9x+zjXqGxf821qz++ld8tp6Z4jUS6qmPZUXH6tfh4riXOhAqb12tWGWAevCFtMt1goLWkQMqIJKpsA==", "dev": true }, "node_modules/@types/mime": { diff --git a/dashboard/package.json b/dashboard/package.json index 11d562a2f..fb1673a80 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -36,7 +36,7 @@ "@commitlint/cli": "^17.2.0", "@commitlint/config-conventional": "^17.2.0", "@jest/globals": "^29.5.0", - "@storybook/addon-essentials": "^7.0.0-beta.52", + "@storybook/addon-essentials": "^7.4.6", "@storybook/addon-interactions": "^7.0.0-beta.52", "@storybook/addon-links": "^7.0.0-beta.52", "@storybook/addons": "^7.0.0-beta.52", From d4ea880ebf7a893811494ab971ab0402856087c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:45:12 +0000 Subject: [PATCH 042/113] chore(deps-dev): bump eslint-plugin-storybook in /dashboard Bumps [eslint-plugin-storybook](https://github.com/storybookjs/eslint-plugin-storybook) from 0.6.13 to 0.6.15. - [Release notes](https://github.com/storybookjs/eslint-plugin-storybook/releases) - [Changelog](https://github.com/storybookjs/eslint-plugin-storybook/blob/main/CHANGELOG.md) - [Commits](https://github.com/storybookjs/eslint-plugin-storybook/compare/v0.6.13...v0.6.15) --- updated-dependencies: - dependency-name: eslint-plugin-storybook dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dashboard/package-lock.json | 8 ++++---- dashboard/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 9ba3612ba..e1a8ab712 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -56,7 +56,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-jest": "^27.4.0", "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-storybook": "^0.6.13", + "eslint-plugin-storybook": "^0.6.15", "husky": "^8.0.0", "jest": "^29.5.0", "jest-environment-jsdom": "^29.6.4", @@ -14097,9 +14097,9 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "0.6.13", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.13.tgz", - "integrity": "sha512-smd+CS0WH1jBqUEJ3znGS7DU4ayBE9z6lkQAK2yrSUv1+rq8BT/tiI5C/rKE7rmiqiAfojtNYZRhzo5HrulccQ==", + "version": "0.6.15", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.15.tgz", + "integrity": "sha512-lAGqVAJGob47Griu29KXYowI4G7KwMoJDOkEip8ujikuDLxU+oWJ1l0WL6F2oDO4QiyUFXvtDkEkISMOPzo+7w==", "dev": true, "dependencies": { "@storybook/csf": "^0.0.1", diff --git a/dashboard/package.json b/dashboard/package.json index 81ba2890b..e8c820094 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -62,7 +62,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-jest": "^27.4.0", "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-storybook": "^0.6.13", + "eslint-plugin-storybook": "^0.6.15", "husky": "^8.0.0", "jest": "^29.5.0", "jest-environment-jsdom": "^29.6.4", From 476b273a97bce35c773f57a9c6a05f9699899e8b Mon Sep 17 00:00:00 2001 From: Shubham Gopale Date: Tue, 10 Oct 2023 18:07:07 +0000 Subject: [PATCH 043/113] add owner tags for jobs and pods (#1064) * add cronJob support Signed-off-by: shubhindia * drop fmt.Printf() Signed-off-by: shubhindia * don't use separate cronJob collector Signed-off-by: shubhindia * use owner_kind instead of owner Signed-off-by: shubhindia * add owner tags for pods as well Signed-off-by: shubhindia --------- Signed-off-by: shubhindia Co-authored-by: Azanul Haque <42029519+Azanul@users.noreply.github.com> --- providers/k8s/core/jobs.go | 17 ++++++++++++++++- providers/k8s/core/pods.go | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/providers/k8s/core/jobs.go b/providers/k8s/core/jobs.go index 5303f4d53..ba1acd67c 100644 --- a/providers/k8s/core/jobs.go +++ b/providers/k8s/core/jobs.go @@ -41,6 +41,21 @@ func Jobs(ctx context.Context, client providers.ProviderClient) ([]models.Resour }) } + if len(job.OwnerReferences) > 0 { + // we use the owner kind of first owner only as the owner tag + ownerTags := []models.Tag{ + { + Key: "owner_kind", + Value: job.OwnerReferences[0].Kind, + }, + { + Key: "owner_name", + Value: job.OwnerReferences[0].Name, + }, + } + tags = append(tags, ownerTags...) + } + cost := 0.0 if opencostEnabled { cost = jobsCost[job.Name].TotalCost @@ -70,7 +85,7 @@ func Jobs(ctx context.Context, client providers.ProviderClient) ([]models.Resour log.WithFields(log.Fields{ "provider": "Kubernetes", "account": client.Name, - "service": "DaemonSet", + "service": "Job", "resources": len(resources), }).Info("Fetched resources") return resources, nil diff --git a/providers/k8s/core/pods.go b/providers/k8s/core/pods.go index 180d8ee12..aa157ad22 100644 --- a/providers/k8s/core/pods.go +++ b/providers/k8s/core/pods.go @@ -41,6 +41,21 @@ func Pods(ctx context.Context, client providers.ProviderClient) ([]models.Resour }) } + if len(pod.OwnerReferences) > 0 { + // we use the owner kind of first owner only as the owner tag + ownerTags := []models.Tag{ + { + Key: "owner_kind", + Value: pod.OwnerReferences[0].Kind, + }, + { + Key: "owner_name", + Value: pod.OwnerReferences[0].Name, + }, + } + tags = append(tags, ownerTags...) + } + cost := 0.0 if opencostEnabled { cost = podsCost[pod.Name].TotalCost From a12ea82def8fe433ee4664c99c7f36ace12c7868 Mon Sep 17 00:00:00 2001 From: jbleduigou Date: Tue, 10 Oct 2023 20:51:59 +0200 Subject: [PATCH 044/113] feat: add support of Kinesis EFO Consumers --- docs/configuration/cloud-providers/aws.mdx | 1 + mocks/kinesis.go | 20 +++ policy.json | 1 + providers/aws/kinesis/streams.go | 46 ++++++ providers/aws/kinesis/streams_test.go | 164 +++++++++++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 mocks/kinesis.go create mode 100644 providers/aws/kinesis/streams_test.go diff --git a/docs/configuration/cloud-providers/aws.mdx b/docs/configuration/cloud-providers/aws.mdx index a924962c5..fc1a827b9 100644 --- a/docs/configuration/cloud-providers/aws.mdx +++ b/docs/configuration/cloud-providers/aws.mdx @@ -33,6 +33,7 @@ sidebar_label: Amazon Web Services - IAM roles - IAM SAML providers - Kinesis Data Streams + - Kinesis EFO Consumers - KMS keys - Lambda functions - RDS clusters diff --git a/mocks/kinesis.go b/mocks/kinesis.go new file mode 100644 index 000000000..28f60b488 --- /dev/null +++ b/mocks/kinesis.go @@ -0,0 +1,20 @@ +package mocks + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/service/kinesis" + "github.com/stretchr/testify/mock" +) + +type KinesisClient struct { + mock.Mock +} + +func (_m *KinesisClient) ListStreamConsumers(ctx context.Context, params *kinesis.ListStreamConsumersInput, optFns ...func(*kinesis.Options)) (*kinesis.ListStreamConsumersOutput, error) { + ret := _m.Called(ctx, params, optFns) + if ret.Get(1) == nil { + return ret.Get(0).(*kinesis.ListStreamConsumersOutput), nil + } + return nil, ret.Get(1).(error) +} diff --git a/policy.json b/policy.json index c39f4dfd9..cd321e529 100644 --- a/policy.json +++ b/policy.json @@ -55,6 +55,7 @@ "iam:ListSAMLProviders", "iam:ListSAMLProviderTags", "kinesis:ListStreams", + "kinesis:ListStreamConsumers", "kms:ListKeys", "kms:ListResourceTags", "kms:DescribeKey", diff --git a/providers/aws/kinesis/streams.go b/providers/aws/kinesis/streams.go index 2a2c31a55..27d1c6c6c 100644 --- a/providers/aws/kinesis/streams.go +++ b/providers/aws/kinesis/streams.go @@ -9,10 +9,15 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/kinesis" + "github.com/aws/aws-sdk-go-v2/service/kinesis/types" . "github.com/tailwarden/komiser/models" . "github.com/tailwarden/komiser/providers" ) +type KinesisClient interface { + ListStreamConsumers(ctx context.Context, params *kinesis.ListStreamConsumersInput, optFns ...func(*kinesis.Options)) (*kinesis.ListStreamConsumersOutput, error) +} + func Streams(ctx context.Context, client ProviderClient) ([]Resource, error) { resources := make([]Resource, 0) var config kinesis.ListStreamsInput @@ -37,6 +42,11 @@ func Streams(ctx context.Context, client ProviderClient) ([]Resource, error) { FetchedAt: time.Now(), Link: fmt.Sprintf("https://%s.console.aws.amazon.com/kinesis/home?region=%s#/streams/details/%s", client.AWSClient.Region, client.AWSClient.Region, *stream.StreamName), }) + consumers, err := getStreamConsumers(kinesisClient, stream, client.Name, client.AWSClient.Region) + if err != nil { + return resources, err + } + resources = append(resources, consumers...) } if aws.ToString(output.NextToken) == "" { @@ -56,3 +66,39 @@ func Streams(ctx context.Context, client ProviderClient) ([]Resource, error) { return resources, nil } + +func getStreamConsumers(kinesisClient KinesisClient, stream types.StreamSummary, clientName, region string) ([]Resource, error) { + resources := make([]Resource, 0) + config := kinesis.ListStreamConsumersInput{ + StreamARN: aws.String(aws.ToString(stream.StreamARN)), + } + + for { + output, err := kinesisClient.ListStreamConsumers(context.Background(), &config) + if err != nil { + return resources, err + } + + for _, consumer := range output.Consumers { + resources = append(resources, Resource{ + Provider: "AWS", + Account: clientName, + Service: "Kinesis EFO Consumer", + ResourceId: *consumer.ConsumerARN, + Region: region, + Name: *consumer.ConsumerName, + Cost: 0, + CreatedAt: *consumer.ConsumerCreationTimestamp, + FetchedAt: time.Now(), + Link: fmt.Sprintf("https://%s.console.aws.amazon.com/kinesis/home?region=%s#/streams/details/%s/registeredConsumers/%s", region, region, aws.ToString(stream.StreamName), *consumer.ConsumerName), + }) + } + + if aws.ToString(output.NextToken) == "" { + break + } + config.NextToken = output.NextToken + } + + return resources, nil +} diff --git a/providers/aws/kinesis/streams_test.go b/providers/aws/kinesis/streams_test.go new file mode 100644 index 000000000..7f10e173b --- /dev/null +++ b/providers/aws/kinesis/streams_test.go @@ -0,0 +1,164 @@ +package kinesis + +import ( + "fmt" + "testing" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/kinesis" + "github.com/aws/aws-sdk-go-v2/service/kinesis/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/tailwarden/komiser/mocks" + . "github.com/tailwarden/komiser/models" +) + +func Test_getStreamConsumers(t *testing.T) { + tests := []struct { + name string + stream types.StreamSummary + setupMock func(m *mocks.KinesisClient) + clientName string + region string + want []Resource + wantErr bool + }{ + { + name: "Should return one EFO consumer", + stream: types.StreamSummary{ + StreamARN: aws.String("arn:aws:kinesis:us-east-1:0123456789:stream/kinesis-data-stream"), + StreamName: aws.String("kinesis-data-stream"), + }, + setupMock: func(m *mocks.KinesisClient) { + m.On("ListStreamConsumers", mock.Anything, mock.Anything, mock.Anything).Return(&kinesis.ListStreamConsumersOutput{ + Consumers: []types.Consumer{ + { + ConsumerARN: aws.String("arn:aws:kinesis:us-east-1:0123456789:stream/kinesis-data-stream/consumer/kinesis-efo-consumer:1234567890"), + ConsumerCreationTimestamp: aws.Time(time.UnixMilli(1234567890)), + ConsumerName: aws.String("kinesis-efo-consumer"), + ConsumerStatus: types.ConsumerStatusActive, + }, + }, + }, nil).Once() + }, + clientName: "sandbox", + region: "us-east-1", + want: []Resource{ + { + Provider: "AWS", + Account: "sandbox", + Service: "Kinesis EFO Consumer", + ResourceId: "arn:aws:kinesis:us-east-1:0123456789:stream/kinesis-data-stream/consumer/kinesis-efo-consumer:1234567890", + Region: "us-east-1", + Name: "kinesis-efo-consumer", + Cost: 0, + CreatedAt: time.UnixMilli(1234567890), + FetchedAt: time.Now(), + Link: "https://us-east-1.console.aws.amazon.com/kinesis/home?region=us-east-1#/streams/details/kinesis-data-stream/registeredConsumers/kinesis-efo-consumer", + }, + }, + wantErr: false, + }, + { + name: "Should paginate using next token", + stream: types.StreamSummary{ + StreamARN: aws.String("arn:aws:kinesis:us-east-1:0123456789:stream/kinesis-data-stream"), + StreamName: aws.String("kinesis-data-stream"), + }, + setupMock: func(m *mocks.KinesisClient) { + m.On("ListStreamConsumers", mock.Anything, mock.Anything, mock.Anything).Return(&kinesis.ListStreamConsumersOutput{ + NextToken: aws.String("next-token"), + Consumers: []types.Consumer{ + { + ConsumerARN: aws.String("arn:aws:kinesis:us-east-1:0123456789:stream/kinesis-data-stream/consumer/kinesis-efo-consumer-1:1234567890"), + ConsumerCreationTimestamp: aws.Time(time.UnixMilli(1234567890)), + ConsumerName: aws.String("kinesis-efo-consumer-1"), + ConsumerStatus: types.ConsumerStatusActive, + }, + }, + }, nil).Once() + m.On("ListStreamConsumers", mock.Anything, mock.Anything, mock.Anything).Return(&kinesis.ListStreamConsumersOutput{ + Consumers: []types.Consumer{ + { + ConsumerARN: aws.String("arn:aws:kinesis:us-east-1:0123456789:stream/kinesis-data-stream/consumer/kinesis-efo-consumer-2:1234567890"), + ConsumerCreationTimestamp: aws.Time(time.UnixMilli(1234567890)), + ConsumerName: aws.String("kinesis-efo-consumer-2"), + ConsumerStatus: types.ConsumerStatusActive, + }, + }, + }, nil).Once() + }, + clientName: "sandbox", + region: "us-east-1", + want: []Resource{ + { + Provider: "AWS", + Account: "sandbox", + Service: "Kinesis EFO Consumer", + ResourceId: "arn:aws:kinesis:us-east-1:0123456789:stream/kinesis-data-stream/consumer/kinesis-efo-consumer-1:1234567890", + Region: "us-east-1", + Name: "kinesis-efo-consumer-1", + Cost: 0, + CreatedAt: time.UnixMilli(1234567890), + FetchedAt: time.Now(), + Link: "https://us-east-1.console.aws.amazon.com/kinesis/home?region=us-east-1#/streams/details/kinesis-data-stream/registeredConsumers/kinesis-efo-consumer-1", + }, + { + Provider: "AWS", + Account: "sandbox", + Service: "Kinesis EFO Consumer", + ResourceId: "arn:aws:kinesis:us-east-1:0123456789:stream/kinesis-data-stream/consumer/kinesis-efo-consumer-2:1234567890", + Region: "us-east-1", + Name: "kinesis-efo-consumer-2", + Cost: 0, + CreatedAt: time.UnixMilli(1234567890), + FetchedAt: time.Now(), + Link: "https://us-east-1.console.aws.amazon.com/kinesis/home?region=us-east-1#/streams/details/kinesis-data-stream/registeredConsumers/kinesis-efo-consumer-2", + }, + }, + wantErr: false, + }, + { + name: "Should return error if error with kinesis client", + stream: types.StreamSummary{ + StreamARN: aws.String("arn:aws:kinesis:us-east-1:0123456789:stream/kinesis-data-stream"), + StreamName: aws.String("kinesis-data-stream"), + }, + setupMock: func(m *mocks.KinesisClient) { + m.On("ListStreamConsumers", mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("unit test error")).Once() + }, + clientName: "sandbox", + region: "us-east-1", + want: []Resource{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + kinesisClient := &mocks.KinesisClient{} + tt.setupMock(kinesisClient) + + got, err := getStreamConsumers(kinesisClient, tt.stream, tt.clientName, tt.region) + if (err != nil) != tt.wantErr { + t.Errorf("getStreamConsumers() error = %v, wantErr %v", err, tt.wantErr) + return + } + if len(got) != len(tt.want) { + t.Errorf("getStreamConsumers() incorrect lenght of resources got = %v, want %v", len(got), len(tt.want)) + } else { + for i := range got { + assert.Equalf(t, tt.want[i].Link, got[i].Link, "incorrect Link for resources") + assert.Equalf(t, tt.want[i].Provider, got[i].Provider, "incorrect Provider for resources") + assert.Equalf(t, tt.want[i].Account, got[i].Account, "incorrect Account for resources") + assert.Equalf(t, tt.want[i].Service, got[i].Service, "incorrect Service for resources") + assert.Equalf(t, tt.want[i].ResourceId, got[i].ResourceId, "incorrect ResourceId for resources") + assert.Equalf(t, tt.want[i].Region, got[i].Region, "incorrect Region for resources") + assert.Equalf(t, tt.want[i].Name, got[i].Name, "incorrect Name for resources") + assert.Equalf(t, tt.want[i].Cost, got[i].Cost, "incorrect Cost for resources") + } + } + kinesisClient.AssertExpectations(t) + }) + } +} From c577fd66c8e74fbeba9d3989919927e9b0858435 Mon Sep 17 00:00:00 2001 From: jbleduigou Date: Wed, 11 Oct 2023 08:28:38 +0200 Subject: [PATCH 045/113] fix: re-use the same context within private functions --- providers/aws/kinesis/streams.go | 6 +++--- providers/aws/kinesis/streams_test.go | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/providers/aws/kinesis/streams.go b/providers/aws/kinesis/streams.go index 27d1c6c6c..de718e6d4 100644 --- a/providers/aws/kinesis/streams.go +++ b/providers/aws/kinesis/streams.go @@ -42,7 +42,7 @@ func Streams(ctx context.Context, client ProviderClient) ([]Resource, error) { FetchedAt: time.Now(), Link: fmt.Sprintf("https://%s.console.aws.amazon.com/kinesis/home?region=%s#/streams/details/%s", client.AWSClient.Region, client.AWSClient.Region, *stream.StreamName), }) - consumers, err := getStreamConsumers(kinesisClient, stream, client.Name, client.AWSClient.Region) + consumers, err := getStreamConsumers(ctx, kinesisClient, stream, client.Name, client.AWSClient.Region) if err != nil { return resources, err } @@ -67,14 +67,14 @@ func Streams(ctx context.Context, client ProviderClient) ([]Resource, error) { return resources, nil } -func getStreamConsumers(kinesisClient KinesisClient, stream types.StreamSummary, clientName, region string) ([]Resource, error) { +func getStreamConsumers(ctx context.Context, kinesisClient KinesisClient, stream types.StreamSummary, clientName, region string) ([]Resource, error) { resources := make([]Resource, 0) config := kinesis.ListStreamConsumersInput{ StreamARN: aws.String(aws.ToString(stream.StreamARN)), } for { - output, err := kinesisClient.ListStreamConsumers(context.Background(), &config) + output, err := kinesisClient.ListStreamConsumers(ctx, &config) if err != nil { return resources, err } diff --git a/providers/aws/kinesis/streams_test.go b/providers/aws/kinesis/streams_test.go index 7f10e173b..20c4790eb 100644 --- a/providers/aws/kinesis/streams_test.go +++ b/providers/aws/kinesis/streams_test.go @@ -1,6 +1,7 @@ package kinesis import ( + "context" "fmt" "testing" "time" @@ -136,10 +137,12 @@ func Test_getStreamConsumers(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + kinesisClient := &mocks.KinesisClient{} tt.setupMock(kinesisClient) - got, err := getStreamConsumers(kinesisClient, tt.stream, tt.clientName, tt.region) + got, err := getStreamConsumers(ctx, kinesisClient, tt.stream, tt.clientName, tt.region) if (err != nil) != tt.wantErr { t.Errorf("getStreamConsumers() error = %v, wantErr %v", err, tt.wantErr) return From a27da11e7622ba99b730c1d2414e8af1883c46e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20R=C3=B6sel?= <320272+Traxmaxx@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:43:46 +0200 Subject: [PATCH 046/113] Feature/tech 1656 (#1043) * feat: Add feedback widget with screenshot and file upload functionality * fix: add preview-api storybook package due to deprecations happening --- .../feedback-widget/FeedbackWidget.tsx | 475 +++++++ dashboard/components/input/Input.tsx | 16 +- .../alerts/InventoryViewAlertsEditAlert.tsx | 3 + dashboard/components/modal/Modal.stories.tsx | 42 + dashboard/components/modal/Modal.tsx | 11 +- dashboard/components/navbar/Navbar.tsx | 12 +- dashboard/package-lock.json | 1237 +++++++++-------- dashboard/package.json | 3 + dashboard/services/settingsService.ts | 13 + 9 files changed, 1184 insertions(+), 628 deletions(-) create mode 100644 dashboard/components/feedback-widget/FeedbackWidget.tsx create mode 100644 dashboard/components/modal/Modal.stories.tsx diff --git a/dashboard/components/feedback-widget/FeedbackWidget.tsx b/dashboard/components/feedback-widget/FeedbackWidget.tsx new file mode 100644 index 000000000..51bf836d4 --- /dev/null +++ b/dashboard/components/feedback-widget/FeedbackWidget.tsx @@ -0,0 +1,475 @@ +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'; + +import Modal from '@components/modal/Modal'; +import Input from '@components/input/Input'; +import settingsService from '@services/settingsService'; +import Button from '@components/button/Button'; +import useToast from '@components/toast/hooks/useToast'; +import Toast from '@components/toast/Toast'; + +// We define the placeholder here for convenience +// It's difficult to read when passed inline +const textAreaPlaceholder = `Example: +Steps to Reproduce +1. Describe the actions you took leading to the bug. +2. Include any specific settings or options you selected. + +Expected Behavior +1. Explain what you expected to happen. +2. Detail how the feature or function should work. + +Outcome +1. Describe what actually happened. +2. Include any error messages or unexpected behavior.`; + +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); + } + + function closeFeedbackModal() { + setShowFeedbackModal(false); + } + + function toggleFeedbackModal() { + setShowFeedbackModal(!showFeedbackModel); + } + + const screenshotModalFilter = (node: HTMLElement) => + !node.id?.startsWith(FEEDBACK_MODAL_ID); + + const FeedbackModal = () => { + const [email, updateEmail] = useState(''); + const [description, updateDescription] = useState(''); + const [isTakingScreenCapture, setIsTakingScreenCapture] = useState(false); + const [fileAttachement, setFileAttachement] = useState(null); + const [isSendingFeedback, setIsSendingFeedback] = useState(false); + const { toast, setToast, dismissToast } = useToast(); + + async function takeScreenshot() { + if ( + document.documentElement === null || + isSendingFeedback || + isTakingScreenCapture || + fileAttachement !== null + ) { + return; + } + setIsTakingScreenCapture(true); + + toBlob(document.documentElement, { + cacheBust: true, + filter: screenshotModalFilter + }) + .then(async blob => { + // setScreenshotBlob(blob); + if (blob !== null) { + const screenShotFile = new File( + [blob], + 'Automated screen capture', + { + type: blob.type + } + ); + + setFileAttachement(screenShotFile); + } + + setToast({ + hasError: false, + title: 'Screen capture', + message: + 'A screenshot of your current page on Komiser has been captured and attached to your feedback.' + }); + }) + .catch(err => { + setToast({ + hasError: true, + title: 'Screen capture failed', + message: + 'The capture of your current page on Komiser couldnโ€™t be saved. Please try again or upload a screenshot manually. Our support is also happy to help you!' + }); + }) + .finally(() => { + setIsTakingScreenCapture(false); + }); + } + + function clearFeedbackForm() { + setFileAttachement(null); + updateDescription(''); + updateEmail(''); + } + + async function uploadFeedback(e: SyntheticEvent) { + if (!isSendingFeedback) { + try { + setIsSendingFeedback(true); + e.preventDefault(); + const formData = new FormData(); + + formData.append('description', description); + if (email) formData.append('email', email); + if (fileAttachement && fileAttachement !== null) + formData.append('image', fileAttachement); + + settingsService + .sendFeedback(formData) + .then(result => { + setToast({ + hasError: false, + title: 'Feedback sent', + message: + result.Response || + 'Your insights are valuable to us. Thank you for helping us improve!' + }); + clearFeedbackForm(); + }) + .catch(error => { + setToast({ + hasError: true, + title: 'Feedback', + message: 'An Error happened. Maybe try again please!' + }); + }) + .finally(() => { + setIsSendingFeedback(false); + }); + } catch { + setIsSendingFeedback(false); + } + } + } + + function uploadFile(attachement: File) { + setFileAttachement(attachement); + } + + return ( + <> + closeFeedbackModal()} + id={FEEDBACK_MODAL_ID} + > +
+

+ Describe your issue +

+

+ By providing details of the issue youโ€™ve encountered and outlining + the steps to reproduce it, weโ€™ll be able to give you better + support. +

+
+ { + updateEmail(change.email); + }} + value={email} + required + /> +