diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..dffddfc31 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,19 @@ +name: Lint +on: + push: + +jobs: + lint: + name: golangci-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=10m + version: v1.54.2 \ No newline at end of file diff --git a/README.md b/README.md index 3848e9fba..ce3a84c85 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,30 @@
+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! ๐คฉ + + + + +- - - -
- -[![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 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 ๐ฐ - - - -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** -- [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) -# 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. * 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. @@ -85,101 +73,105 @@ 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. -# 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. +## Getting started -## Installation on AWS +### Installation -### 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 +#### Tailwarden Cloud (Free & Recommended) -### 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). +Head over to [Tailwarden](https://cloud.tailwarden.com?utm_source=github&utm_medium=social&utm_campaign=readme). -Watch the installation [video here](https://www.youtube.com/watch?v=4veDmJpui44&t) +#### Linux -### 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). - -## 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. - -## 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 Civo - -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. +``` +wget https://cli.komiser.io/latest/komiser_Linux_x86_64 -O komiser +``` -## Installation on Digital Ocean +#### Windows -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. +``` +wget https://cli.komiser.io/latest/komiser_Windows_x86_64.zip +tar -xf komiser_Windows_x86_64.zip +``` -## Installation on Linode +#### Mac OS X -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. +ARM architecture (M1 & M2 Chip) +``` +wget https://cli.komiser.io/latest/komiser_Darwin_arm64 -O komiser +``` -## Installation on Tencent Cloud +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 [**Tencent**](https://docs.komiser.io/docs/cloud-providers/tencent?utm_source=github&utm_medium=social) account. +#### Homebrew +``` +brew update +brew tap tailwarden/komiser +brew install komiser -## Installation on Scaleway +# Make sure you are running the newest version of Komiser: +brew update +brew reinstall komiser +``` -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. +### Configuration +| 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) | [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) | [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) | | +| [![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) | | -# 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) +* [Videos](https://www.youtube.com/@tailwarden/videos) +* [Blog](https://www.tailwarden.com/blog) -## 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 ๐ +## 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). 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", 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/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/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/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) } 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/providers/k8s/core/daemonsets.go b/providers/k8s/core/daemonsets.go new file mode 100644 index 000000000..0cff39cb7 --- /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 +} 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 +} 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..60226960b 100644 --- a/providers/k8s/k8s.go +++ b/providers/k8s/k8s.go @@ -22,6 +22,9 @@ func listOfSupportedServices() []providers.FetchDataFunction { core.ServiceAccounts, core.Nodes, core.Namespaces, + core.DaemonSets, + core.StatefulSets, + core.Jobs, } } 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