Add experimental support for Kyverno policies (#2655)

Related to #2653

Signed-off-by: Sergio Castaño Arteaga <tegioz@icloud.com>
Signed-off-by: Cintia Sanchez Garcia <cynthiasg@icloud.com>
Co-authored-by: Sergio Castaño Arteaga <tegioz@icloud.com>
Co-authored-by: Cintia Sanchez Garcia <cynthiasg@icloud.com>
This commit is contained in:
Sergio Castaño Arteaga 2023-01-10 19:23:17 +01:00 committed by GitHub
parent 3b6922a005
commit 6c797abaa2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 554 additions and 38 deletions

View File

@ -24,6 +24,7 @@ At the moment, the following artifacts kinds are supported *(with plans to suppo
- [Keptn integrations](https://keptn.sh) - [Keptn integrations](https://keptn.sh)
- [Kubectl plugins (Krew)](https://krew.sigs.k8s.io/) - [Kubectl plugins (Krew)](https://krew.sigs.k8s.io/)
- [Kubewarden policies](https://www.kubewarden.io) - [Kubewarden policies](https://www.kubewarden.io)
- [Kyverno policies](https://kyverno.io)
- [OLM operators](https://github.com/operator-framework) - [OLM operators](https://github.com/operator-framework)
- [Open Policy Agent (OPA) policies](https://www.openpolicyagent.org/) - [Open Policy Agent (OPA) policies](https://www.openpolicyagent.org/)
- [Tekton tasks and pipelines](https://tekton.dev/) - [Tekton tasks and pipelines](https://tekton.dev/)

View File

@ -2,7 +2,7 @@ apiVersion: v2
name: artifact-hub name: artifact-hub
description: Artifact Hub is a web-based application that enables finding, installing, and publishing Kubernetes packages. description: Artifact Hub is a web-based application that enables finding, installing, and publishing Kubernetes packages.
type: application type: application
version: 1.11.1-3 version: 1.11.1-4
appVersion: 1.11.0 appVersion: 1.11.0
kubeVersion: ">= 1.19.0-0" kubeVersion: ">= 1.19.0-0"
home: https://artifacthub.io home: https://artifacthub.io
@ -22,6 +22,7 @@ keywords:
- containers images - containers images
- kubewarden - kubewarden
- gatekeeper - gatekeeper
- kyverno
maintainers: maintainers:
- name: Sergio - name: Sergio
email: tegioz@icloud.com email: tegioz@icloud.com

View File

@ -1054,7 +1054,7 @@
}, },
"repositoriesKinds": { "repositoriesKinds": {
"title": "Repositories kinds to process ([] = all)", "title": "Repositories kinds to process ([] = all)",
"description": "The following kinds are supported at the moment: falco, helm, olm, opa, tbaction, krew, helm-plugin, tekton-task, keda-scaler, coredns, keptn, tekton-pipeline, container, kubewarden, gatekeeper", "description": "The following kinds are supported at the moment: falco, helm, olm, opa, tbaction, krew, helm-plugin, tekton-task, keda-scaler, coredns, keptn, tekton-pipeline, container, kubewarden, gatekeeper, kyverno",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"

View File

@ -0,0 +1,5 @@
insert into repository_kind values (15, 'Kyverno policies');
---- create above / drop below ----
delete from repository_kind where repository_kind_id = 15;

View File

@ -521,7 +521,8 @@ select results_eq(
(11, 'Tekton pipelines'), (11, 'Tekton pipelines'),
(12, 'Containers images'), (12, 'Containers images'),
(13, 'Kubewarden policies'), (13, 'Kubewarden policies'),
(14, 'Gatekeeper policies') (14, 'Gatekeeper policies'),
(15, 'Kyverno policies')
$$, $$,
'Repository kinds should exist' 'Repository kinds should exist'
); );

View File

@ -1341,6 +1341,29 @@ paths:
$ref: "#/components/responses/TooManyRequests" $ref: "#/components/responses/TooManyRequests"
"500": "500":
$ref: "#/components/responses/InternalServerError" $ref: "#/components/responses/InternalServerError"
"/packages/kyverno/{repoName}/{packageName}":
get:
tags:
- Packages
summary: Get package details
description: Get package details
operationId: getKyvernoPoliciesDetails
parameters:
- $ref: "#/components/parameters/RepoNameParam"
- $ref: "#/components/parameters/PackageNameParam"
responses:
"200":
description: ""
content:
application/json:
schema:
$ref: "#/components/schemas/KyvernoPolicy"
"404":
$ref: "#/components/responses/NotFoundResponse"
"429":
$ref: "#/components/responses/TooManyRequests"
"500":
$ref: "#/components/responses/InternalServerError"
"/packages/opa/{repoName}/{packageName}": "/packages/opa/{repoName}/{packageName}":
get: get:
tags: tags:
@ -1696,6 +1719,30 @@ paths:
$ref: "#/components/responses/TooManyRequests" $ref: "#/components/responses/TooManyRequests"
"500": "500":
$ref: "#/components/responses/InternalServerError" $ref: "#/components/responses/InternalServerError"
"/packages/kyverno/{repoName}/{packageName}/{version}":
get:
tags:
- Packages
summary: Get package version details
description: Get package version details
operationId: getKyvernoPoliciesVersionDetails
parameters:
- $ref: "#/components/parameters/RepoNameParam"
- $ref: "#/components/parameters/PackageNameParam"
- $ref: "#/components/parameters/VersionParam"
responses:
"200":
description: ""
content:
application/json:
schema:
$ref: "#/components/schemas/KyvernoPolicy"
"404":
$ref: "#/components/responses/NotFoundResponse"
"429":
$ref: "#/components/responses/TooManyRequests"
"500":
$ref: "#/components/responses/InternalServerError"
"/packages/opa/{repoName}/{packageName}/{version}": "/packages/opa/{repoName}/{packageName}/{version}":
get: get:
tags: tags:
@ -3581,6 +3628,43 @@ components:
kubewarden/mutation: kubewarden/mutation:
type: string type: string
example: "true" example: "true"
KyvernoPolicy:
allOf:
- $ref: "#/components/schemas/Package"
- type: object
properties:
relative_path:
type: string
nullable: false
example: /general/allowedrepos
data:
type: object
nullable: false
properties:
policy:
type: string
nullable: false
example: >-
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-certificates-volume
annotations:
policies.kyverno.io/title: Add Certificates as a Volume
policies.kyverno.io/category: Sample
policies.kyverno.io/subject: Pod,Volume
kyverno/subject:
type: string
example: "Pod, Volume"
kyverno/version:
type: string
example: "1.5.0"
kyverno/kubernetesVersion:
type: string
example: "1.21"
kyverno/category:
type: string
example: "OpenShift"
Link: Link:
type: object type: object
nullable: false nullable: false
@ -4168,6 +4252,7 @@ components:
* `12` - Container images * `12` - Container images
* `13` - Kubewarden policies * `13` - Kubewarden policies
* `14` - Gatekeeper policies * `14` - Gatekeeper policies
* `15` - Kyverno policies
RepositoryKindParam: RepositoryKindParam:
type: string type: string
enum: enum:
@ -4186,6 +4271,7 @@ components:
- container - container
- kubewarden - kubewarden
- gatekeeper - gatekeeper
- kyverno
description: | description: |
Repository kind name: Repository kind name:
* `helm` - Helm charts * `helm` - Helm charts
@ -4203,6 +4289,7 @@ components:
* `container` - Container images * `container` - Container images
* `kubewarden` - Kubewarden policies * `kubewarden` - Kubewarden policies
* `gatekeeper` - Gatekeeper policies * `gatekeeper` - Gatekeeper policies
* `kyverno` - Kyverno policies
RepositorySummary: RepositorySummary:
type: object type: object
required: required:
@ -4700,6 +4787,7 @@ components:
* `12` - Container images * `12` - Container images
* `13` - Kubewarden policies * `13` - Kubewarden policies
* `14` - Gatekeeper policies * `14` - Gatekeeper policies
* `15` - Kyverno policies
PackageNameParam: PackageNameParam:
in: path in: path
name: packageName name: packageName

View File

@ -0,0 +1,32 @@
# Kyverno annotations
You can provide some extra information about your Kyverno policies by using the `annotations` field in the [Artifact Hub package metadata file](https://github.com/artifacthub/hub/blob/master/docs/metadata/artifacthub-pkg.yml).
## Supported annotations
- **kyverno/category** *(string)*
Category the policy fits in. Unless the policy applies to a community or "external" Kubernetes project, use other.
- **kyverno/kubernetesVersion** *(string)*
Version(s) of Kubernetes against which the policy should work. Value should ideally be a range of versions no more than two prior (ex., 1.19-1.21) and must be enclosed in quotes.
- **kyverno/subject** *(string)*
The focus of the policy. For example, Pod or Ingress or a CustomResource like ClusterIssuer. The subject is the "thing" on which the policy operates. For multiple, use a comma-separated string like Pod, Deployment.
- **kyverno/version** *(string)*
Minimum version of Kyverno where this policy works. Note that this isn't the version of Kyverno where it was developed or tested but the minimum version of Kyverno where it's supported. If unknown, omit.
## Example
```yaml
...
annotations:
kyverno/category: "OpenShift"
kyverno/kubernetesVersion: "1.20"
kyverno/subject: "Route"
kyverno/version: "1.6.0"
```

View File

@ -0,0 +1,49 @@
## Kyverno policies repositories
Kyverno policies repositories are expected to be hosted in GitHub, GitLab or Bitbucket repos. When adding your repository to Artifact Hub, the url used **must** follow the following format:
- `https://github.com/user/repo[/path/to/packages]`
- `https://gitlab.com/user/repo[/path/to/packages]`
- `https://bitbucket.org/user/repo[/path/to/packages]`
By default the `master` branch is used, but it's possible to specify a different one from the UI.
*Please NOTE that the repository URL used when adding the repository to Artifact Hub **must NOT** contain the git hosting platform specific parts, like **tree/branch**, just the path to your packages like it would show in the filesystem.*
The *path/to/packages* provided can contain metadata for one or more packages. Each package version **must** be on a separate folder, and it's up to you to decide if you want to publish one or multiple versions of your package.
The structure of a repository with multiple policies packages and versions could look something like this:
```sh
$ tree path/to/packages
path/to/packages
├── artifacthub-repo.yml
├── package1
│   ├── 1.0.0
│   │   ├── README.md
│   │   └── artifacthub-pkg.yml
│   └── 2.0.0
│      ├── README.md
│   └── artifacthub-pkg.yml
└── package2
└── 1.0.0
      ├── README.md
└── artifacthub-pkg.yml
```
This structure is flexible, and in some cases it can be greatly simplified. In the case of a single package with a single version available at a time (the publisher doesn't want to make previous ones available, for example), the structure could look like this:
```sh
$ tree path/to/packages
path/to/packages
├── artifacthub-repo.yml
└── package1
   ├── README.md
└── artifacthub-pkg.yml
```
In the previous case, even the `package1` directory could be omitted. The reason is that both packages names and versions are read from the `artifacthub-pkg.yml` metadata file, so directories names are not used at all.
Each package version **needs** an `artifacthub-pkg.yml` metadata file. Please see the file [spec](https://github.com/artifacthub/hub/blob/master/docs/metadata/artifacthub-pkg.yml) for more details. The [artifacthub-repo.yml](https://github.com/artifacthub/hub/blob/master/docs/metadata/artifacthub-repo.yml) repository metadata file shown above can be used to setup features like [Verified publisher](https://github.com/artifacthub/hub/blob/master/docs/repositories.md#verified-publisher) or [Ownership claim](https://github.com/artifacthub/hub/blob/master/docs/repositories.md#ownership-claim). This file must be located at `/path/to/packages`.
Once you have added your repository, you are all set up. As you add new versions of your policies packages or even new packages to your git repository, they'll be automatically indexed and listed in Artifact Hub.

View File

@ -13,6 +13,7 @@ The following repositories kinds are supported at the moment:
- [Keptn integrations repositories](https://github.com/artifacthub/hub/blob/master/docs/keptn_integrations_repositories.md) - [Keptn integrations repositories](https://github.com/artifacthub/hub/blob/master/docs/keptn_integrations_repositories.md)
- [Krew kubectl plugins repositories](https://github.com/artifacthub/hub/blob/master/docs/krew_kubectl_plugins_repositories.md) - [Krew kubectl plugins repositories](https://github.com/artifacthub/hub/blob/master/docs/krew_kubectl_plugins_repositories.md)
- [Kubewarden policies repositories](https://github.com/artifacthub/hub/blob/master/docs/kubewarden_policies_repositories.md) - [Kubewarden policies repositories](https://github.com/artifacthub/hub/blob/master/docs/kubewarden_policies_repositories.md)
- [Kyverno policies repositories](https://github.com/artifacthub/hub/blob/master/docs/kyverno_policies_repositories.md)
- [OLM operators repositories](https://github.com/artifacthub/hub/blob/master/docs/olm_operators_repositories.md) - [OLM operators repositories](https://github.com/artifacthub/hub/blob/master/docs/olm_operators_repositories.md)
- [OPA policies repositories](https://github.com/artifacthub/hub/blob/master/docs/opa_policies_repositories.md) - [OPA policies repositories](https://github.com/artifacthub/hub/blob/master/docs/opa_policies_repositories.md)
- [Tekton pipelines repositories](https://github.com/artifacthub/hub/blob/master/docs/tekton_pipelines_repositories.md) - [Tekton pipelines repositories](https://github.com/artifacthub/hub/blob/master/docs/tekton_pipelines_repositories.md)

View File

@ -13,11 +13,12 @@ The documentation is organized in the following topics:
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Repositories guide](/docs/topics/repositories) | The repositories guide explains how to add repositories to Artifact Hub, as well as other related concepts like Verified publisher or Ownership Claim. | | [Repositories guide](/docs/topics/repositories) | The repositories guide explains how to add repositories to Artifact Hub, as well as other related concepts like Verified publisher or Ownership Claim. |
| [Helm annotations](/docs/topics/annotations/helm) | Describes some custom annotations that allow enriching the existing metadata in Helm Charts to improve users' experience in Artifact Hub. | | [Helm annotations](/docs/topics/annotations/helm) | Describes some custom annotations that allow enriching the existing metadata in Helm Charts to improve users' experience in Artifact Hub. |
| [OLM annotations](/docs/topics/annotations/olm) | Describes some custom annotations that allow enriching the existing metadata in OLM operators to improve users' experience in Artifact Hub. | | [Keptn annotations](/docs/topics/annotations/keptn) | Describes some custom annotations that allow enriching the existing metadata in Keptn integrations to improve users' experience in Artifact Hub. |
| [Krew annotations](/docs/topics/annotations/krew) | Describes some custom annotations that allow enriching the existing metadata in Krew kubectl plugins to improve users' experience in Artifact Hub. | | [Krew annotations](/docs/topics/annotations/krew) | Describes some custom annotations that allow enriching the existing metadata in Krew kubectl plugins to improve users' experience in Artifact Hub. |
| [Tekton annotations](/docs/topics/annotations/tekton) | Describes some custom annotations that allow enriching the existing metadata in Tekton tasks to improve users' experience in Artifact Hub. |
| [Kubewarden annotations](/docs/topics/annotations/kubewarden) | Describes some custom annotations that allow enriching the existing metadata in Kubewarden policies to improve users' experience in Artifact Hub. | | [Kubewarden annotations](/docs/topics/annotations/kubewarden) | Describes some custom annotations that allow enriching the existing metadata in Kubewarden policies to improve users' experience in Artifact Hub. |
| [keptn annotations](/docs/topics/annotations/keptn) | Describes some custom annotations that allow enriching the existing metadata in Keptn integrations to improve users' experience in Artifact Hub. | | [Kyverno annotations](/docs/topics/annotations/kyverno) | Describes some custom annotations that allow enriching the existing metadata in Kyverno policies to improve users' experience in Artifact Hub. |
| [OLM annotations](/docs/topics/annotations/olm) | Describes some custom annotations that allow enriching the existing metadata in OLM operators to improve users' experience in Artifact Hub. |
| [Tekton annotations](/docs/topics/annotations/tekton) | Describes some custom annotations that allow enriching the existing metadata in Tekton tasks to improve users' experience in Artifact Hub. |
| [Packages security report](/docs/topics/security_report) | Explains how packages are scanned for security vulnerabilities and the structure of the security report. | | [Packages security report](/docs/topics/security_report) | Explains how packages are scanned for security vulnerabilities and the structure of the security report. |
| [Authorization](/docs/topics/authorization) | Explains how the authorization mechanism that allows organizations to define what actions can be performed by their members works and how to set it up. | | [Authorization](/docs/topics/authorization) | Explains how the authorization mechanism that allows organizations to define what actions can be performed by their members works and how to set it up. |
| [Architecture](/docs/topics/architecture) | Describes the components that form Artifact Hub, what each of them do and the layout of the source repository. | | [Architecture](/docs/topics/architecture) | Describes the components that form Artifact Hub, what each of them do and the layout of the source repository. |

View File

@ -1,6 +1,6 @@
--- ---
title: "Keptn" title: "Keptn"
weight: 4 weight: 2
aliases: [ aliases: [
"/keptn_annotations", "/keptn_annotations",
] ]

View File

@ -0,0 +1,7 @@
---
title: "Kyverno"
weight: 5
aliases: [
"/kyverno_annotations"
]
---

View File

@ -0,0 +1,7 @@
---
title: "Kyverno policies"
weight: 11
aliases: [
"/kyverno_policies_repositories",
]
---

View File

@ -1,6 +1,6 @@
--- ---
title: "OLM" title: "OLM"
weight: 2 weight: 6
aliases: [ aliases: [
"/olm_annotations", "/olm_annotations",
] ]

View File

@ -1,6 +1,6 @@
--- ---
title: "OLM operators" title: "OLM operators"
weight: 11 weight: 12
aliases: [ aliases: [
"/olm_operators_repositories", "/olm_operators_repositories",
] ]

View File

@ -1,6 +1,6 @@
--- ---
title: "OPA policies" title: "OPA policies"
weight: 12 weight: 13
aliases: [ aliases: [
"/opa_policies_repositories", "/opa_policies_repositories",
] ]

View File

@ -1,6 +1,6 @@
--- ---
title: "Tekton" title: "Tekton"
weight: 4 weight: 7
aliases: [ aliases: [
"/tekton_annotations", "/tekton_annotations",
] ]

View File

@ -1,6 +1,6 @@
--- ---
title: "Tekton pipelines" title: "Tekton pipelines"
weight: 13 weight: 14
aliases: [ aliases: [
"/tekton_pipelines_repositories", "/tekton_pipelines_repositories",
] ]

View File

@ -1,6 +1,6 @@
--- ---
title: "Tekton tasks" title: "Tekton tasks"
weight: 14 weight: 15
aliases: [ aliases: [
"/tekton_tasks_repositories" "/tekton_tasks_repositories"
] ]

View File

@ -1,6 +1,6 @@
--- ---
title: "Tinkerbell actions" title: "Tinkerbell actions"
weight: 15 weight: 16
aliases: [ aliases: [
"/tinkerbell_actions_repositories", "/tinkerbell_actions_repositories",
] ]

View File

@ -264,7 +264,7 @@ func (h *Handlers) setupRouter() {
r.Get("/stats", h.Packages.GetStats) r.Get("/stats", h.Packages.GetStats)
r.With(corsMW).Get("/search", h.Packages.Search) r.With(corsMW).Get("/search", h.Packages.Search)
r.With(h.Users.RequireLogin).Get("/starred", h.Packages.GetStarredByUser) r.With(h.Users.RequireLogin).Get("/starred", h.Packages.GetStarredByUser)
r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew|^helm-plugin|^tekton-task|^keda-scaler|^coredns|^keptn|^tekton-pipeline|^container|^kubewarden|^gatekeeper$}/{repoName}/{packageName}", func(r chi.Router) { r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew|^helm-plugin|^tekton-task|^keda-scaler|^coredns|^keptn|^tekton-pipeline|^container|^kubewarden|^gatekeeper|^kyverno$}/{repoName}/{packageName}", func(r chi.Router) {
r.Get("/feed/rss", h.Packages.RssFeed) r.Get("/feed/rss", h.Packages.RssFeed)
r.With(corsMW).Get("/summary", h.Packages.GetSummary) r.With(corsMW).Get("/summary", h.Packages.GetSummary)
r.Get("/{version}", h.Packages.Get) r.Get("/{version}", h.Packages.Get)
@ -421,7 +421,7 @@ func (h *Handlers) setupRouter() {
// Index special entry points // Index special entry points
r.Route("/packages", func(r chi.Router) { r.Route("/packages", func(r chi.Router) {
r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew|^helm-plugin|^tekton-task|^keda-scaler|^coredns|^keptn|^tekton-pipeline|^container|^kubewarden|^gatekeeper$}/{repoName}/{packageName}", func(r chi.Router) { r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew|^helm-plugin|^tekton-task|^keda-scaler|^coredns|^keptn|^tekton-pipeline|^container|^kubewarden|^gatekeeper|^kyverno$}/{repoName}/{packageName}", func(r chi.Router) {
r.With(h.Packages.InjectIndexMeta).Get("/{version}", h.Static.Index) r.With(h.Packages.InjectIndexMeta).Get("/{version}", h.Static.Index)
r.With(h.Packages.InjectIndexMeta).Get("/", h.Static.Index) r.With(h.Packages.InjectIndexMeta).Get("/", h.Static.Index)
}) })

View File

@ -2047,6 +2047,17 @@ func TestBuildURL(t *testing.T) {
"2.0.0", "2.0.0",
baseURL + "/packages/gatekeeper/repo1/pkg1/2.0.0", baseURL + "/packages/gatekeeper/repo1/pkg1/2.0.0",
}, },
{
&hub.Package{
NormalizedName: "pkg1",
Repository: &hub.Repository{
Kind: hub.Kyverno,
Name: "repo1",
},
},
"2.0.0",
baseURL + "/packages/kyverno/repo1/pkg1/2.0.0",
},
} }
for _, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc

View File

@ -91,6 +91,9 @@ const (
// Gatekeeper represents a repository with Gatekeeper policies. // Gatekeeper represents a repository with Gatekeeper policies.
Gatekeeper RepositoryKind = 14 Gatekeeper RepositoryKind = 14
// Kyverno represents a repository with Kyverno policies.
Kyverno RepositoryKind = 15
) )
// GetKindName returns the name of the provided repository kind. // GetKindName returns the name of the provided repository kind.
@ -116,6 +119,8 @@ func GetKindName(kind RepositoryKind) string {
return "krew" return "krew"
case Kubewarden: case Kubewarden:
return "kubewarden" return "kubewarden"
case Kyverno:
return "kyverno"
case OLM: case OLM:
return "olm" return "olm"
case OPA: case OPA:
@ -155,6 +160,8 @@ func GetKindFromName(kind string) (RepositoryKind, error) {
return Krew, nil return Krew, nil
case "kubewarden": case "kubewarden":
return Kubewarden, nil return Kubewarden, nil
case "kyverno":
return Kyverno, nil
case "olm": case "olm":
return OLM, nil return OLM, nil
case "opa": case "opa":

View File

@ -88,6 +88,7 @@ var (
hub.Keptn, hub.Keptn,
hub.Krew, hub.Krew,
hub.Kubewarden, hub.Kubewarden,
hub.Kyverno,
hub.OLM, hub.OLM,
hub.OPA, hub.OPA,
hub.TBAction, hub.TBAction,
@ -280,6 +281,7 @@ func (m *Manager) ClaimOwnership(ctx context.Context, repoName, orgName string)
hub.Keptn, hub.Keptn,
hub.Krew, hub.Krew,
hub.Kubewarden, hub.Kubewarden,
hub.Kyverno,
hub.OLM, hub.OLM,
hub.OPA, hub.OPA,
hub.TBAction, hub.TBAction,
@ -452,6 +454,7 @@ func (m *Manager) locateMetadataFile(r *hub.Repository, basePath string) string
hub.Keptn, hub.Keptn,
hub.Krew, hub.Krew,
hub.Kubewarden, hub.Kubewarden,
hub.Kyverno,
hub.OLM, hub.OLM,
hub.OPA, hub.OPA,
hub.TBAction, hub.TBAction,
@ -808,6 +811,7 @@ func (m *Manager) validateURL(r *hub.Repository) error {
hub.Keptn, hub.Keptn,
hub.Krew, hub.Krew,
hub.Kubewarden, hub.Kubewarden,
hub.Kyverno,
hub.OLM, hub.OLM,
hub.OPA, hub.OPA,
hub.TBAction, hub.TBAction,

View File

@ -112,7 +112,15 @@ func SetupSource(i *hub.TrackerSourceInput) hub.TrackerSource {
source = krew.NewTrackerSource(i) source = krew.NewTrackerSource(i)
case hub.OLM: case hub.OLM:
source = olm.NewTrackerSource(i) source = olm.NewTrackerSource(i)
case hub.OPA, hub.TBAction, hub.KedaScaler, hub.CoreDNS, hub.Keptn, hub.Kubewarden, hub.Gatekeeper: case
hub.CoreDNS,
hub.Gatekeeper,
hub.KedaScaler,
hub.Keptn,
hub.Kubewarden,
hub.Kyverno,
hub.OPA,
hub.TBAction:
source = generic.NewTrackerSource(i) source = generic.NewTrackerSource(i)
case hub.TektonTask, hub.TektonPipeline: case hub.TektonTask, hub.TektonPipeline:
source = tekton.NewTrackerSource(i) source = tekton.NewTrackerSource(i)

View File

@ -29,6 +29,10 @@ const (
// that contains the template. // that contains the template.
GatekeeperTemplateKey = "template" GatekeeperTemplateKey = "template"
// KyvernoPolicyKey represents the key used in the package's data field that
// contains the raw policy.
KyvernoPolicyKey = "policy"
// OPAPoliciesKey represents the key used in the package's data field that // OPAPoliciesKey represents the key used in the package's data field that
// contains the raw policies. // contains the raw policies.
OPAPoliciesKey = "policies" OPAPoliciesKey = "policies"
@ -191,6 +195,8 @@ func PreparePackage(r *hub.Repository, md *hub.PackageMetadata, pkgPath string)
kindData, err = prepareFalcoData(pkgPath, ignorer) kindData, err = prepareFalcoData(pkgPath, ignorer)
case hub.Gatekeeper: case hub.Gatekeeper:
kindData, err = prepareGatekeeperData(pkgPath) kindData, err = prepareGatekeeperData(pkgPath)
case hub.Kyverno:
kindData, err = prepareKyvernoData(pkgPath)
case hub.OPA: case hub.OPA:
kindData, err = prepareOPAData(pkgPath, ignorer) kindData, err = prepareOPAData(pkgPath, ignorer)
} }
@ -283,6 +289,22 @@ func prepareGatekeeperData(pkgPath string) (map[string]interface{}, error) {
}, nil }, nil
} }
// prepareKyernoData reads and formats Kyverno specific data available in the
// path provided, returning the resulting data structure.
func prepareKyvernoData(pkgPath string) (map[string]interface{}, error) {
// Read policy file
policyPath := path.Join(pkgPath, path.Base(pkgPath)+".yaml")
policy, err := os.ReadFile(policyPath)
if err != nil {
return nil, fmt.Errorf("error reading kyverno policy file: %w", err)
}
// Return package data field
return map[string]interface{}{
KyvernoPolicyKey: string(policy),
}, nil
}
// prepareOPAData reads and formats OPA specific data available in the path // prepareOPAData reads and formats OPA specific data available in the path
// provided, returning the resulting data structure. // provided, returning the resulting data structure.
func prepareOPAData(pkgPath string, ignorer ignore.IgnoreParser) (map[string]interface{}, error) { func prepareOPAData(pkgPath string, ignorer ignore.IgnoreParser) (map[string]interface{}, error) {

View File

@ -184,6 +184,7 @@ func (t *Tracker) cloneRepository() (string, string, error) {
hub.Keptn, hub.Keptn,
hub.Krew, hub.Krew,
hub.Kubewarden, hub.Kubewarden,
hub.Kyverno,
hub.OPA, hub.OPA,
hub.TBAction, hub.TBAction,
hub.TektonPipeline, hub.TektonPipeline,

View File

@ -16,6 +16,7 @@ cat docs/www/headers/keda_scalers_repositories docs/keda_scalers_repositories.md
cat docs/www/headers/keptn_integrations_repositories docs/keptn_integrations_repositories.md > docs/www/content/topics/repositories/keptn-integrations.md cat docs/www/headers/keptn_integrations_repositories docs/keptn_integrations_repositories.md > docs/www/content/topics/repositories/keptn-integrations.md
cat docs/www/headers/krew_kubectl_plugins_repositories docs/krew_kubectl_plugins_repositories.md > docs/www/content/topics/repositories/krew-kubectl-plugins.md cat docs/www/headers/krew_kubectl_plugins_repositories docs/krew_kubectl_plugins_repositories.md > docs/www/content/topics/repositories/krew-kubectl-plugins.md
cat docs/www/headers/kubewarden_policies_repositories docs/kubewarden_policies_repositories.md > docs/www/content/topics/repositories/kubewarden-policies.md cat docs/www/headers/kubewarden_policies_repositories docs/kubewarden_policies_repositories.md > docs/www/content/topics/repositories/kubewarden-policies.md
cat docs/www/headers/kyverno_policies_repositories docs/kyverno_policies_repositories.md > docs/www/content/topics/repositories/kyverno-policies.md
cat docs/www/headers/olm_operators_repositories docs/olm_operators_repositories.md > docs/www/content/topics/repositories/olm-operators.md cat docs/www/headers/olm_operators_repositories docs/olm_operators_repositories.md > docs/www/content/topics/repositories/olm-operators.md
cat docs/www/headers/opa_policies_repositories docs/opa_policies_repositories.md > docs/www/content/topics/repositories/opa-policies.md cat docs/www/headers/opa_policies_repositories docs/opa_policies_repositories.md > docs/www/content/topics/repositories/opa-policies.md
cat docs/www/headers/tekton_pipelines_repositories docs/tekton_pipelines_repositories.md > docs/www/content/topics/repositories/tekton-pipelines.md cat docs/www/headers/tekton_pipelines_repositories docs/tekton_pipelines_repositories.md > docs/www/content/topics/repositories/tekton-pipelines.md
@ -24,8 +25,9 @@ cat docs/www/headers/tinkerbell_actions_repositories docs/tinkerbell_actions_rep
cat docs/www/headers/security_report docs/security_report.md > docs/www/content/topics/security_report.md cat docs/www/headers/security_report docs/security_report.md > docs/www/content/topics/security_report.md
cat docs/www/headers/cli docs/cli.md > docs/www/content/topics/cli.md cat docs/www/headers/cli docs/cli.md > docs/www/content/topics/cli.md
cat docs/www/headers/helm_annotations docs/helm_annotations.md > docs/www/content/topics/annotations/helm.md cat docs/www/headers/helm_annotations docs/helm_annotations.md > docs/www/content/topics/annotations/helm.md
cat docs/www/headers/olm_annotations docs/olm_annotations.md > docs/www/content/topics/annotations/olm.md
cat docs/www/headers/krew_annotations docs/krew_annotations.md > docs/www/content/topics/annotations/krew.md
cat docs/www/headers/tekton_annotations docs/tekton_annotations.md > docs/www/content/topics/annotations/tekton.md
cat docs/www/headers/keptn_annotations docs/keptn_annotations.md > docs/www/content/topics/annotations/keptn.md cat docs/www/headers/keptn_annotations docs/keptn_annotations.md > docs/www/content/topics/annotations/keptn.md
cat docs/www/headers/krew_annotations docs/krew_annotations.md > docs/www/content/topics/annotations/krew.md
cat docs/www/headers/kubewarden_annotations docs/kubewarden_annotations.md > docs/www/content/topics/annotations/kubewarden.md cat docs/www/headers/kubewarden_annotations docs/kubewarden_annotations.md > docs/www/content/topics/annotations/kubewarden.md
cat docs/www/headers/kyverno_annotations docs/kyverno_annotations.md > docs/www/content/topics/annotations/kyverno.md
cat docs/www/headers/olm_annotations docs/olm_annotations.md > docs/www/content/topics/annotations/olm.md
cat docs/www/headers/tekton_annotations docs/tekton_annotations.md > docs/www/content/topics/annotations/tekton.md

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@ -58,6 +58,8 @@ const Image = (props: Props) => {
return '/static/media/placeholder_pkg_kubewarden.png'; return '/static/media/placeholder_pkg_kubewarden.png';
case RepositoryKind.Gatekeeper: case RepositoryKind.Gatekeeper:
return '/static/media/placeholder_pkg_gatekeeper.png'; return '/static/media/placeholder_pkg_gatekeeper.png';
case RepositoryKind.Kyverno:
return '/static/media/placeholder_pkg_kyverno.png';
default: default:
return PLACEHOLDER_SRC; return PLACEHOLDER_SRC;
} }

View File

@ -114,6 +114,13 @@ describe('RepositoryIcon', () => {
expect(icon).toHaveProperty('src', 'http://localhost/static/media/gatekeeper-light.svg'); expect(icon).toHaveProperty('src', 'http://localhost/static/media/gatekeeper-light.svg');
}); });
it('renders Kyverno policy icon', () => {
render(<RepositoryIcon kind={RepositoryKind.Kyverno} type="white" />);
const icon = screen.getByAltText('Icon');
expect(icon).toBeInTheDocument();
expect(icon).toHaveProperty('src', 'http://localhost/static/media/kyverno-light.svg');
});
it('renders Chart icon - default type', () => { it('renders Chart icon - default type', () => {
render(<RepositoryIcon kind={RepositoryKind.Helm} />); render(<RepositoryIcon kind={RepositoryKind.Helm} />);
const icons = screen.getAllByAltText('Icon'); const icons = screen.getAllByAltText('Icon');

View File

@ -69,6 +69,10 @@ const ICONS = {
default: '/static/media/gatekeeper.svg', default: '/static/media/gatekeeper.svg',
white: '/static/media/gatekeeper-light.svg', white: '/static/media/gatekeeper-light.svg',
}, },
[RepositoryKind.Kyverno]: {
default: '/static/media/kyverno.svg',
white: '/static/media/kyverno-light.svg',
},
}; };
const RepositoryIcon = (props: Props) => { const RepositoryIcon = (props: Props) => {

View File

@ -74,6 +74,10 @@ jest.mock('../../utils/getSampleQueries', () => () => {
name: 'Gatekeeper policies', name: 'Gatekeeper policies',
querystring: 'kind=14', querystring: 'kind=14',
}, },
{
name: 'Kyverno policies',
querystring: 'kind=15',
},
]; ];
}); });

View File

@ -409,6 +409,17 @@ const RepositoryModal = (props: Props) => {
</ExternalLink> </ExternalLink>
); );
break; break;
case RepositoryKind.Kyverno:
link = (
<ExternalLink
href="/docs/topics/repositories/kyverno-policies"
className="text-primary fw-bold"
label="Open documentation"
>
Kyverno policies
</ExternalLink>
);
break;
} }
if (isUndefined(link)) return; if (isUndefined(link)) return;
@ -431,6 +442,7 @@ const RepositoryModal = (props: Props) => {
case RepositoryKind.TektonPipeline: case RepositoryKind.TektonPipeline:
case RepositoryKind.Kubewarden: case RepositoryKind.Kubewarden:
case RepositoryKind.Gatekeeper: case RepositoryKind.Gatekeeper:
case RepositoryKind.Kyverno:
return ( return (
<> <>
<p <p
@ -817,6 +829,7 @@ const RepositoryModal = (props: Props) => {
RepositoryKind.TektonPipeline, RepositoryKind.TektonPipeline,
RepositoryKind.Kubewarden, RepositoryKind.Kubewarden,
RepositoryKind.Gatekeeper, RepositoryKind.Gatekeeper,
RepositoryKind.Kyverno,
].includes(selectedKind) && ( ].includes(selectedKind) && (
<div> <div>
<InputField <InputField
@ -956,6 +969,7 @@ const RepositoryModal = (props: Props) => {
RepositoryKind.Container, RepositoryKind.Container,
RepositoryKind.Kubewarden, RepositoryKind.Kubewarden,
RepositoryKind.Gatekeeper, RepositoryKind.Gatekeeper,
RepositoryKind.Kyverno,
].includes(selectedKind) && ( ].includes(selectedKind) && (
<div className="mt-4 mb-3"> <div className="mt-4 mb-3">
<div className="form-check form-switch ps-0"> <div className="form-check form-switch ps-0">

View File

@ -109,6 +109,11 @@ exports[`Repository Modal - repositories section creates snapshot 1`] = `
> >
Kubewarden policies Kubewarden policies
</option> </option>
<option
value="15"
>
Kyverno policies
</option>
<option <option
value="3" value="3"
> >

View File

@ -330,7 +330,7 @@ exports[`Home index creates snapshot 1`] = `
<div <div
class="text-center px-3 px-xs-0" class="text-center px-3 px-xs-0"
> >
Artifact Hub is a web-based application that enables finding, installing, and publishing packages and configurations for CNCF projects. For example, this could include Helm charts and plugins, Falco configurations, Open Policy Agent (OPA) and Gatekeeper policies, OLM operators, Tinkerbell actions, kubectl plugins, Tekton tasks and pipelines, KEDA scalers, CoreDNS plugins, Keptn integrations, container images, and Kubewarden policies. Artifact Hub is a web-based application that enables finding, installing, and publishing packages and configurations for CNCF projects. For example, this could include Helm charts and plugins, Falco configurations, Open Policy Agent (OPA) and Gatekeeper policies, OLM operators, Tinkerbell actions, kubectl plugins, Tekton tasks and pipelines, KEDA scalers, CoreDNS plugins, Keptn integrations, container images, Kubewarden policies and Kyverno policies.
<div <div
class="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around" class="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around"
> >
@ -466,6 +466,10 @@ exports[`Home index creates snapshot 1`] = `
</div> </div>
</div> </div>
</a> </a>
</div>
<div
class="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around"
>
<a <a
aria-label="Open Krew site" aria-label="Open Krew site"
class="link col iconLink" class="link col iconLink"
@ -492,10 +496,6 @@ exports[`Home index creates snapshot 1`] = `
</div> </div>
</div> </div>
</a> </a>
</div>
<div
class="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around"
>
<a <a
aria-label="Open Tekton site" aria-label="Open Tekton site"
class="link col iconLink" class="link col iconLink"
@ -600,6 +600,10 @@ exports[`Home index creates snapshot 1`] = `
</div> </div>
</div> </div>
</a> </a>
</div>
<div
class="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around"
>
<a <a
aria-label="Open Container Initiative site" aria-label="Open Container Initiative site"
class="link col iconLink" class="link col iconLink"
@ -652,6 +656,32 @@ exports[`Home index creates snapshot 1`] = `
</div> </div>
</div> </div>
</a> </a>
<a
aria-label="Open Kubewarden site"
class="link col iconLink"
href="https://www.kyverno.io"
rel="noopener noreferrer"
role="button"
tabindex="-1"
target="_blank"
>
<div
class="d-flex flex-column justify-content-between align-items-center h-100"
>
<img
alt="Icon"
class="aboutIcon"
src="/static/media/kyverno-light.svg"
/>
<div
class="d-none d-sm-block text-light mt-2 legendIcon"
>
<small>
Kyverno policies
</small>
</div>
</div>
</a>
</div> </div>
Discovering artifacts to use with CNCF projects can be difficult. If every CNCF project that needs to share artifacts creates its own Hub this creates a fair amount of repeat work for each project and a fractured experience for those trying to find the artifacts to consume. The Artifact Hub attempts to solve that by providing a single experience for consumers that any CNCF project can leverage. Discovering artifacts to use with CNCF projects can be difficult. If every CNCF project that needs to share artifacts creates its own Hub this creates a fair amount of repeat work for each project and a fractured experience for those trying to find the artifacts to consume. The Artifact Hub attempts to solve that by providing a single experience for consumers that any CNCF project can leverage.
</div> </div>

View File

@ -131,7 +131,7 @@ describe('Home index', () => {
await waitFor(() => expect(API.getStats).toHaveBeenCalledTimes(1)); await waitFor(() => expect(API.getStats).toHaveBeenCalledTimes(1));
const links = await screen.findAllByRole('button'); const links = await screen.findAllByRole('button');
expect(links).toHaveLength(19); expect(links).toHaveLength(20);
expect(links[2]).toHaveProperty('href', 'https://github.com/artifacthub/hub'); expect(links[2]).toHaveProperty('href', 'https://github.com/artifacthub/hub');
expect(links[3]).toHaveProperty('href', 'https://cloud-native.slack.com/channels/artifact-hub'); expect(links[3]).toHaveProperty('href', 'https://cloud-native.slack.com/channels/artifact-hub');
@ -153,8 +153,9 @@ describe('Home index', () => {
expect(links[15]).toHaveProperty('href', 'https://keptn.sh/'); expect(links[15]).toHaveProperty('href', 'https://keptn.sh/');
expect(links[16]).toHaveProperty('href', 'https://opencontainers.org/'); expect(links[16]).toHaveProperty('href', 'https://opencontainers.org/');
expect(links[17]).toHaveProperty('href', 'https://www.kubewarden.io/'); expect(links[17]).toHaveProperty('href', 'https://www.kubewarden.io/');
expect(links[18]).toHaveProperty('href', 'https://www.kyverno.io/');
expect(links[18]).toHaveProperty('href', 'https://www.cncf.io/sandbox-projects/'); expect(links[19]).toHaveProperty('href', 'https://www.cncf.io/sandbox-projects/');
}); });
}); });
}); });

View File

@ -229,7 +229,7 @@ const HomeView = (props: Props) => {
configurations for CNCF projects. For example, this could include Helm charts and plugins, Falco configurations for CNCF projects. For example, this could include Helm charts and plugins, Falco
configurations, Open Policy Agent (OPA) and Gatekeeper policies, OLM operators, Tinkerbell actions, configurations, Open Policy Agent (OPA) and Gatekeeper policies, OLM operators, Tinkerbell actions,
kubectl plugins, Tekton tasks and pipelines, KEDA scalers, CoreDNS plugins, Keptn integrations, kubectl plugins, Tekton tasks and pipelines, KEDA scalers, CoreDNS plugins, Keptn integrations,
container images, and Kubewarden policies. container images, Kubewarden policies and Kyverno policies.
<div className="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around"> <div className="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around">
<ExternalLink href="https://helm.sh" className={`col ${styles.iconLink}`} label="Open Helm site"> <ExternalLink href="https://helm.sh" className={`col ${styles.iconLink}`} label="Open Helm site">
<div className="d-flex flex-column justify-content-between align-items-center h-100"> <div className="d-flex flex-column justify-content-between align-items-center h-100">
@ -283,6 +283,8 @@ const HomeView = (props: Props) => {
</div> </div>
</div> </div>
</ExternalLink> </ExternalLink>
</div>
<div className="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around">
<ExternalLink <ExternalLink
href="https://krew.sigs.k8s.io" href="https://krew.sigs.k8s.io"
className={`col ${styles.iconLink}`} className={`col ${styles.iconLink}`}
@ -295,8 +297,6 @@ const HomeView = (props: Props) => {
</div> </div>
</div> </div>
</ExternalLink> </ExternalLink>
</div>
<div className="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around">
<ExternalLink href="https://tekton.dev" className={`col ${styles.iconLink}`} label="Open Tekton site"> <ExternalLink href="https://tekton.dev" className={`col ${styles.iconLink}`} label="Open Tekton site">
<div className="d-flex flex-column justify-content-between align-items-center h-100"> <div className="d-flex flex-column justify-content-between align-items-center h-100">
<RepositoryIcon kind={RepositoryKind.TektonTask} type="white" className={styles.aboutIcon} /> <RepositoryIcon kind={RepositoryKind.TektonTask} type="white" className={styles.aboutIcon} />
@ -325,7 +325,6 @@ const HomeView = (props: Props) => {
</div> </div>
</div> </div>
</ExternalLink> </ExternalLink>
<ExternalLink href="https://keptn.sh" className={`col ${styles.iconLink}`} label="Open Keptn site"> <ExternalLink href="https://keptn.sh" className={`col ${styles.iconLink}`} label="Open Keptn site">
<div className="d-flex flex-column justify-content-between align-items-center h-100"> <div className="d-flex flex-column justify-content-between align-items-center h-100">
<RepositoryIcon kind={RepositoryKind.Keptn} type="white" className={styles.aboutIcon} /> <RepositoryIcon kind={RepositoryKind.Keptn} type="white" className={styles.aboutIcon} />
@ -334,6 +333,8 @@ const HomeView = (props: Props) => {
</div> </div>
</div> </div>
</ExternalLink> </ExternalLink>
</div>
<div className="mx-0 mx-md-3 mx-lg-5 my-4 my-sm-5 d-flex flex-row align-items-stretch justify-content-around">
<ExternalLink <ExternalLink
href="https://opencontainers.org" href="https://opencontainers.org"
className={`col ${styles.iconLink}`} className={`col ${styles.iconLink}`}
@ -358,18 +359,18 @@ const HomeView = (props: Props) => {
</div> </div>
</div> </div>
</ExternalLink> </ExternalLink>
{/* <ExternalLink <ExternalLink
href="https://open-policy-agent.github.io/gatekeeper/website/docs/howto/" href="https://www.kyverno.io"
className={`col ${styles.iconLink}`} className={`col ${styles.iconLink}`}
label="Open Gatekeeper site" label="Open Kubewarden site"
> >
<div className="d-flex flex-column justify-content-between align-items-center h-100"> <div className="d-flex flex-column justify-content-between align-items-center h-100">
<RepositoryIcon kind={RepositoryKind.Gatekeeper} type="white" className={styles.aboutIcon} /> <RepositoryIcon kind={RepositoryKind.Kyverno} type="white" className={styles.aboutIcon} />
<div className={`d-none d-sm-block text-light mt-2 ${styles.legendIcon}`}> <div className={`d-none d-sm-block text-light mt-2 ${styles.legendIcon}`}>
<small>Gatekeeper policies</small> <small>Kyverno policies</small>
</div> </div>
</div> </div>
</ExternalLink> */} </ExternalLink>
</div> </div>
Discovering artifacts to use with CNCF projects can be difficult. If every CNCF project that needs to Discovering artifacts to use with CNCF projects can be difficult. If every CNCF project that needs to
share artifacts creates its own Hub this creates a fair amount of repeat work for each project and a share artifacts creates its own Hub this creates a fair amount of repeat work for each project and a

View File

@ -8,6 +8,7 @@ import {
HelmChartType, HelmChartType,
KeptnData, KeptnData,
KubewardenData, KubewardenData,
KyvernoData,
Package, Package,
PackageViewsStats, PackageViewsStats,
RepositoryKind, RepositoryKind,
@ -275,6 +276,58 @@ const Details = (props: Props) => {
)} )}
</> </>
); );
case RepositoryKind.Kyverno:
const subjects: string[] =
props.package.data &&
!isUndefined(props.package.data[KyvernoData.Subject]) &&
props.package.data[KyvernoData.Subject] !== ''
? props.package.data[KyvernoData.Subject]!.split(',')
: [];
return (
<>
{props.package.data && props.package.data.kyvernoVersion && (
<div>
<SmallTitle text="Minimal version" />
<p data-testid="appVersion" className="text-truncate">
{props.package.data.kyvernoVersion}
</p>
</div>
)}
{props.package.data && props.package.data.kyvernoKubernetesVersion && (
<div>
<SmallTitle text="Kubernetes version" />
<p data-testid="kubernetesVersion" className="text-truncate">
{props.package.data.kyvernoKubernetesVersion}
</p>
</div>
)}
{props.package.data && props.package.data.kyvernoCategory && (
<div>
<SmallTitle text="Category" />
<p data-testid="category" className="text-truncate">
{props.package.data.kyvernoCategory}
</p>
</div>
)}
{subjects.length > 0 && (
<div>
<SmallTitle text="Subjects" />
{subjects.map((subject: string, index: number) => (
<p
data-testid="kyvernoSubject"
className={classnames('text-truncate', { 'mb-1': index + 1 !== subjects.length })}
key={`kyverno-subject-${subject}`}
>
{subject}
</p>
))}
</div>
)}
</>
);
case RepositoryKind.TektonTask: case RepositoryKind.TektonTask:
return ( return (
<> <>

View File

@ -440,6 +440,20 @@ const PackageView = (props: Props) => {
return manifest; return manifest;
}; };
const getKyvernoPolicy = (): string | undefined => {
let policy: string | undefined;
if (
!isUndefined(detail) &&
!isNull(detail) &&
!isNull(detail.data) &&
!isUndefined(detail.data) &&
!isUndefined(detail.data.policy)
) {
policy = detail.data.policy as string;
}
return policy;
};
const getGatekeeperTemplate = (): string | undefined => { const getGatekeeperTemplate = (): string | undefined => {
let manifest: string | undefined; let manifest: string | undefined;
if ( if (
@ -657,6 +671,48 @@ const PackageView = (props: Props) => {
</> </>
); );
case RepositoryKind.Kyverno:
let policy: string | undefined = getKyvernoPolicy();
if (!isUndefined(policy)) {
additionalTitles += '# Policy\n';
}
return (
<>
{!isUndefined(policy) && (
<div className={`mb-5 ${styles.codeWrapper}`}>
<AnchorHeader level={2} scrollIntoView={scrollIntoView} title="Policy" />
<div
className={`d-flex d-xxxl-inline-block mw-100 position-relative overflow-hidden border ${styles.manifestWrapper}`}
>
<BlockCodeButtons content={policy} filename={`${detail.normalizedName}-policy.yaml`} />
<SyntaxHighlighter
language="yaml"
style={docco}
customStyle={{
backgroundColor: 'transparent',
padding: '1.5rem',
lineHeight: '1.25rem',
marginBottom: '0',
height: '100%',
fontSize: '80%',
color: '#636a6e',
}}
lineNumberStyle={{
color: 'var(--color-black-25)',
marginRight: '5px',
fontSize: '0.8rem',
}}
showLineNumbers
>
{policy}
</SyntaxHighlighter>
</div>
</div>
)}
</>
);
default: default:
return null; return null;
} }

View File

@ -16,6 +16,7 @@ export enum RepositoryKind {
Container, Container,
Kubewarden, Kubewarden,
Gatekeeper, Gatekeeper,
Kyverno,
} }
export enum KeptnData { export enum KeptnData {
@ -28,6 +29,13 @@ export enum KubewardenData {
Mutation = 'kubewardenMutation', Mutation = 'kubewardenMutation',
} }
export enum KyvernoData {
Subject = 'kyvernoSubject',
Version = 'kyvernoVersion',
KubernetesVersion = 'kyvernoKubernetesVersion',
Category = 'kyvernoCategory',
}
export enum HelmChartType { export enum HelmChartType {
Library = 'library', Library = 'library',
Application = 'application', Application = 'application',
@ -214,10 +222,15 @@ export interface PackageData {
apiVersion?: string; apiVersion?: string;
type?: HelmChartType; type?: HelmChartType;
kubeVersion?: string; kubeVersion?: string;
policy?: string;
[KeptnData.Version]?: string; [KeptnData.Version]?: string;
[KeptnData.Kind]?: string; [KeptnData.Kind]?: string;
[KubewardenData.Resources]?: string; [KubewardenData.Resources]?: string;
[KubewardenData.Mutation]?: string; [KubewardenData.Mutation]?: string;
[KyvernoData.Subject]?: string;
[KyvernoData.Version]?: string;
[KyvernoData.Category]?: string;
[KyvernoData.KubernetesVersion]?: string;
tasks?: TektonTaskInPipeline[]; tasks?: TektonTaskInPipeline[];
alternativeLocations?: string[]; alternativeLocations?: string[];
} }

View File

@ -178,6 +178,15 @@ export const REPOSITORY_KINDS: RepoKindDef[] = [
icon: <RepositoryIcon kind={RepositoryKind.Kubewarden} className="mw-100 mh-100" />, icon: <RepositoryIcon kind={RepositoryKind.Kubewarden} className="mw-100 mh-100" />,
active: true, active: true,
}, },
{
kind: RepositoryKind.Kyverno,
label: 'kyverno',
name: 'Kyverno policies',
singular: 'Kyverno policy',
plural: 'Kyverno policies',
icon: <RepositoryIcon kind={RepositoryKind.Kyverno} className="mw-100 mh-100" />,
active: true,
},
{ {
kind: RepositoryKind.OLM, kind: RepositoryKind.OLM,
label: 'olm', label: 'olm',
@ -777,4 +786,4 @@ export const CVSS_V3_VECTORS: { [key: string]: CVSSVectorMetric[] } = {
export const OCI_PREFIX = 'oci://'; export const OCI_PREFIX = 'oci://';
export const PKG_DETAIL_PATH = export const PKG_DETAIL_PATH =
/^\/packages\/(helm|falco|opa|olm|tbaction|krew|helm-plugin|tekton-task|keda-scaler|coredns|keptn|tekton-pipeline|kubewarden|gatekeeper|container)\//; /^\/packages\/(helm|falco|opa|olm|tbaction|krew|helm-plugin|tekton-task|keda-scaler|coredns|keptn|tekton-pipeline|kubewarden|gatekeeper|kyverno|container)\//;

View File

@ -63,6 +63,10 @@ describe('repoKind', () => {
expect(methods.getRepoKind('gatekeeper')).toBe(RepositoryKind.Gatekeeper); expect(methods.getRepoKind('gatekeeper')).toBe(RepositoryKind.Gatekeeper);
}); });
it('kyverno', () => {
expect(methods.getRepoKind('kyverno')).toBe(RepositoryKind.Kyverno);
});
it('unknown', () => { it('unknown', () => {
expect(methods.getRepoKind('unknown')).toBeNull(); expect(methods.getRepoKind('unknown')).toBeNull();
}); });
@ -129,6 +133,10 @@ describe('repoKind', () => {
expect(methods.getRepoKindName(RepositoryKind.Gatekeeper)).toBe('gatekeeper'); expect(methods.getRepoKindName(RepositoryKind.Gatekeeper)).toBe('gatekeeper');
}); });
it('kyverno kind', () => {
expect(methods.getRepoKindName(RepositoryKind.Kyverno)).toBe('kyverno');
});
it('unknown kind', () => { it('unknown kind', () => {
expect(methods.getRepoKindName(20)).toBeNull(); expect(methods.getRepoKindName(20)).toBeNull();
}); });

View File

@ -32,6 +32,8 @@ const getRepoKind = (repoName: string): RepositoryKind | null => {
return RepositoryKind.Kubewarden; return RepositoryKind.Kubewarden;
case 'gatekeeper': case 'gatekeeper':
return RepositoryKind.Gatekeeper; return RepositoryKind.Gatekeeper;
case 'kyverno':
return RepositoryKind.Kyverno;
default: default:
return null; return null;
} }
@ -69,6 +71,8 @@ const getRepoKindName = (repoKind: RepositoryKind): string | null => {
return 'kubewarden'; return 'kubewarden';
case RepositoryKind.Gatekeeper: case RepositoryKind.Gatekeeper:
return 'gatekeeper'; return 'gatekeeper';
case RepositoryKind.Kyverno:
return 'kyverno';
default: default:
return null; return null;
} }

View File

@ -9,6 +9,7 @@
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div class="artifacthub-widget-group" data-url="http://localhost:3000/packages/search" data-theme="light" data-header="false" data-stars="false" data-color="#417598" data-responsive="true" data-loading="true"></div>
<div class="artifacthub-widget" data-url="http://localhost:8000/packages/null/kubewarden-test/capabilities-psp" data-theme="light" data-header="true" data-stars="true" data-responsive="false"><blockquote><p lang="en" dir="ltr"><b>Capabilities PSP</b>: A Pod Security Policy that controls Container Capabilities</p>&mdash; Open in <a href="http://localhost:3000/packages/null/kubewarden-test/capabilities-psp">Artifact Hub</a></blockquote></div> <div class="artifacthub-widget" data-url="http://localhost:8000/packages/null/kubewarden-test/capabilities-psp" data-theme="light" data-header="true" data-stars="true" data-responsive="false"><blockquote><p lang="en" dir="ltr"><b>Capabilities PSP</b>: A Pod Security Policy that controls Container Capabilities</p>&mdash; Open in <a href="http://localhost:3000/packages/null/kubewarden-test/capabilities-psp">Artifact Hub</a></blockquote></div>
<div class="artifacthub-widget" data-url="https://artifacthub.io/packages/helm/artifact-hub/artifact-hub" data-theme="light" data-header="true" data-stars="false" data-responsive="false"><blockquote><p lang="en" dir="ltr"><b>artifact-hub</b>: Artifact Hub is a web-based application that enables finding, installing, and publishing Kubernetes packages.</p>&mdash; Open in <a href="https://artifacthub.io/packages/helm/artifact-hub/artifact-hub">Artifact Hub</a></blockquote></div> <div class="artifacthub-widget" data-url="https://artifacthub.io/packages/helm/artifact-hub/artifact-hub" data-theme="light" data-header="true" data-stars="false" data-responsive="false"><blockquote><p lang="en" dir="ltr"><b>artifact-hub</b>: Artifact Hub is a web-based application that enables finding, installing, and publishing Kubernetes packages.</p>&mdash; Open in <a href="https://artifacthub.io/packages/helm/artifact-hub/artifact-hub">Artifact Hub</a></blockquote></div>
<div class="artifacthub-widget-group" data-url="https://artifacthub.io/api/v1/packages/search" data-theme="dark" data-header="false" data-stars="false" data-color="#417598" data-responsive="true" data-loading="true"></div> <div class="artifacthub-widget-group" data-url="https://artifacthub.io/api/v1/packages/search" data-theme="dark" data-header="false" data-stars="false" data-color="#417598" data-responsive="true" data-loading="true"></div>

View File

@ -79,6 +79,8 @@ const getRepoKindName = (repoKind: RepositoryKind): string | null => {
return 'kubewarden'; return 'kubewarden';
case RepositoryKind.Gatekeeper: case RepositoryKind.Gatekeeper:
return 'gatekeeper'; return 'gatekeeper';
case RepositoryKind.Kyverno:
return 'kyverno';
default: default:
return null; return null;
} }

View File

@ -130,6 +130,13 @@ describe('Image', () => {
expect(image).toHaveProperty('src', 'https://localhost:8000/static/media/placeholder_pkg_gatekeeper.png'); expect(image).toHaveProperty('src', 'https://localhost:8000/static/media/placeholder_pkg_gatekeeper.png');
}); });
it('renders Kyverno icon', () => {
render(<Image {...defaultProps} kind={RepositoryKind.Kyverno} />);
const image = screen.getByAltText('alt image');
expect(image).toBeInTheDocument();
expect(image).toHaveProperty('src', 'https://localhost:8000/static/media/placeholder_pkg_kyverno.png');
});
it('renders placeholder icon', () => { it('renders placeholder icon', () => {
render(<Image {...defaultProps} placeholderIcon={<>icon</>} />); render(<Image {...defaultProps} placeholderIcon={<>icon</>} />);
expect(screen.getByText('icon')).toBeInTheDocument(); expect(screen.getByText('icon')).toBeInTheDocument();

View File

@ -59,6 +59,8 @@ const Image = (props: Props) => {
return '/static/media/placeholder_pkg_kubewarden.png'; return '/static/media/placeholder_pkg_kubewarden.png';
case RepositoryKind.Gatekeeper: case RepositoryKind.Gatekeeper:
return '/static/media/placeholder_pkg_gatekeeper.png'; return '/static/media/placeholder_pkg_gatekeeper.png';
case RepositoryKind.Kyverno:
return '/static/media/placeholder_pkg_kyverno.png';
default: default:
return PLACEHOLDER_SRC; return PLACEHOLDER_SRC;
} }

View File

@ -27,6 +27,7 @@ const ICONS: IconsList = {
[RepositoryKind.Container]: <SVGIcons name="container" />, [RepositoryKind.Container]: <SVGIcons name="container" />,
[RepositoryKind.Kubewarden]: <SVGIcons name="kubewarden" />, [RepositoryKind.Kubewarden]: <SVGIcons name="kubewarden" />,
[RepositoryKind.Gatekeeper]: <SVGIcons name="gatekeeper" />, [RepositoryKind.Gatekeeper]: <SVGIcons name="gatekeeper" />,
[RepositoryKind.Kyverno]: <SVGIcons name="kyverno" />,
}; };
const RepositoryIcon = (props: Props) => ( const RepositoryIcon = (props: Props) => (

View File

@ -76,6 +76,10 @@ const REPOSITORY_KINDS: RepoKindDef[] = [
kind: RepositoryKind.Gatekeeper, kind: RepositoryKind.Gatekeeper,
name: 'Gatekeeper policy', name: 'Gatekeeper policy',
}, },
{
kind: RepositoryKind.Kyverno,
name: 'Kyverno policy',
},
]; ];
const Wrapper = styled('span')` const Wrapper = styled('span')`

View File

@ -98,6 +98,11 @@ describe('SVGIcons', () => {
expect(screen.getByTitle('gatekeeper')); expect(screen.getByTitle('gatekeeper'));
}); });
it('renders kyverno icon', () => {
render(<SVGIcons name="kyverno" />);
expect(screen.getByTitle('kyverno'));
});
it('does not render when name is not in the list', () => { it('does not render when name is not in the list', () => {
render(<SVGIcons name="not-listed" />); render(<SVGIcons name="not-listed" />);
expect(screen.getByTestId('iconWrapper')).toBeEmptyDOMElement(); expect(screen.getByTestId('iconWrapper')).toBeEmptyDOMElement();

View File

@ -849,6 +849,38 @@ const SVGIcons = (props: Props) => (
</svg> </svg>
); );
case 'kyverno':
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
<title>{props.name}</title>
<defs>
<style>{'.cls-1,.cls-2{fill:#e87e5b;fill-rule:evenodd}.cls-2{fill:#3784c5}'}</style>
</defs>
<g id="Layer_1" data-name="Layer 1">
<path
className="cls-1"
d="M85.978 159.13 62.8 260.676h23.85l19.444-85.188c-3.677-3.731-7.447-7.312-11.34-10.64a10.639 10.639 0 0 1-2.572-3.256c-2.006-.77-4.09-1.595-6.204-2.462m145.446-91.785-119.312 57.457a293.357 293.357 0 0 1 4.348 6.27 10.64 10.64 0 0 1 3.75 1.774c3.838 2.837 7.863 5.519 12.004 8.09l99.368-47.851c.557-4.798.958-9.572 1.125-14.296a10.664 10.664 0 0 1 .946-4.039 282.645 282.645 0 0 1-2.23-7.405Zm161.272 56.727L274.884 67.337a283.621 283.621 0 0 1-2.23 7.413 10.67 10.67 0 0 1 .946 4.04c.166 4.72.568 9.493 1.125 14.29l98.023 47.202c4.155-2.626 8.19-5.364 12.031-8.257a10.688 10.688 0 0 1 3.737-1.808 286.995 286.995 0 0 1 4.18-6.145Zm50.824 136.604-23.506-102.991a285.281 285.281 0 0 1-6.955 2.841 10.693 10.693 0 0 1-2.542 3.277c-3.652 3.177-7.192 6.579-10.646 10.12l19.802 86.753Z"
/>
<path
className="cls-2"
d="m310.806 197.984 2.83 3.494c1.313-11.17 10.58-21.888 28.167-31.497 18.333-10.016 35.454-18.902 49.41-29.41l1.851 2.283q16.362-25.555 25.417-32.173c8.112-4.937 15.609-3.5 21.437 3.004 5.15 7.053 4.998 14.684-1.518 21.593q-8.356 7.481-36.756 18.171l1.849 2.283c-13.178 11.471-25.431 26.369-39.042 42.219-13.051 15.206-25.463 22.04-36.664 21.004l2.831 3.495-11.007 8.912-9.727-23.484-7.474-3.095Zm-135.645 24.808 2.798-3.517c-11.188 1.135-23.66-5.59-36.846-20.682-13.745-15.732-26.127-30.522-39.408-41.876l1.83-2.3q-28.491-10.443-36.914-17.85c-6.574-6.851-6.793-14.481-1.705-21.58 5.77-6.553 13.253-8.056 21.41-3.19q9.11 6.54 25.695 31.951l1.83-2.3c14.05 10.384 31.247 19.122 49.666 28.978 17.672 9.452 27.031 20.091 28.442 31.25l2.8-3.52 8.46 6.73-7.222 2.992-9.81 23.687Zm62.252-45.003h4.497c-7.854-8.05-10.355-21.999-6.753-41.712 3.755-20.552 7.622-39.449 8.24-56.909h2.941q-9.565-28.8-9.01-40c1.269-9.41 7.104-14.334 15.825-14.77 8.722.436 14.557 5.36 15.827 14.77q.55 11.2-9.012 40h2.941c.62 17.46 4.487 36.357 8.24 56.909 3.602 19.713 1.102 33.661-6.752 41.712h4.497v13.113l-15.954-6.607-15.527 6.43Z"
/>
<path
className="cls-1"
d="m252.939 193.893 25.077 10.388 25.081 10.383 10.382 25.083 8.672 20.929h-45.427l-2.017-4.87-3.733-9.019-9.018-3.732-9.017-3.735-9.016 3.735-9.018 3.732-3.733 9.019-2.017 4.87h-45.426l8.671-20.929 10.383-25.083 25.081-10.383 25.075-10.388z"
/>
<path
className="cls-2"
d="M68.214 349.624a8.914 8.914 0 1 1-17.83 0v-63.035a8.916 8.916 0 0 1 8.916-8.914h384.218a8.915 8.915 0 0 1 8.914 8.914v63.035a8.914 8.914 0 1 1-17.829 0v-54.12H260.322v54.12a8.914 8.914 0 1 1-17.829 0v-54.12H68.213Z"
/>
<path
className="cls-1"
d="M241.915 443.208a1.181 1.181 0 0 0 .957.49h8.536l8.536-.002a1.174 1.174 0 0 0 .923-.447l5.323-6.672 5.322-6.675a1.188 1.188 0 0 0 .227-.994l-3.795-16.632a1.165 1.165 0 0 0-.641-.815l-15.381-7.407a1.179 1.179 0 0 0-1.027 0l-15.381 7.407a1.187 1.187 0 0 0-.638.803l-3.79 16.601a1.184 1.184 0 0 0 .218 1.038Zm-12.064 24.65a2.686 2.686 0 0 0 2.174 1.107h19.385l19.385-.004a2.67 2.67 0 0 0 2.097-1.013l12.088-15.153 12.085-15.16a2.708 2.708 0 0 0 .517-2.257l-8.62-37.77a2.65 2.65 0 0 0-1.457-1.851l-34.931-16.823a2.68 2.68 0 0 0-2.329 0l-34.93 16.823a2.677 2.677 0 0 0-1.449 1.823l-8.607 37.7a2.681 2.681 0 0 0 .494 2.357ZM48.641 443.208a1.178 1.178 0 0 0 .957.49h8.536l8.536-.002a1.177 1.177 0 0 0 .924-.447l5.323-6.672 5.32-6.675a1.187 1.187 0 0 0 .228-.994l-3.797-16.632a1.167 1.167 0 0 0-.64-.815l-15.382-7.407a1.176 1.176 0 0 0-1.026 0l-15.38 7.407a1.182 1.182 0 0 0-.638.803l-3.79 16.601a1.176 1.176 0 0 0 .218 1.038Zm-12.063 24.65a2.685 2.685 0 0 0 2.173 1.107h19.383l19.387-.004a2.676 2.676 0 0 0 2.098-1.013l12.086-15.153 12.084-15.16a2.693 2.693 0 0 0 .517-2.257l-8.62-37.77a2.648 2.648 0 0 0-1.455-1.851l-34.93-16.823a2.683 2.683 0 0 0-2.33 0l-34.932 16.823a2.686 2.686 0 0 0-1.449 1.823l-8.604 37.7a2.671 2.671 0 0 0 .494 2.357ZM432.86 443.208a1.178 1.178 0 0 0 .956.49h8.536l8.536-.002a1.177 1.177 0 0 0 .924-.447l5.323-6.672 5.32-6.675a1.181 1.181 0 0 0 .228-.994l-3.795-16.632a1.165 1.165 0 0 0-.642-.815l-15.38-7.407a1.179 1.179 0 0 0-1.027 0l-15.382 7.407a1.19 1.19 0 0 0-.638.803l-3.79 16.601a1.184 1.184 0 0 0 .219 1.038Zm-12.065 24.65a2.686 2.686 0 0 0 2.174 1.107h19.383l19.386-.004a2.674 2.674 0 0 0 2.099-1.013l12.086-15.153 12.084-15.16a2.692 2.692 0 0 0 .516-2.257l-8.618-37.77a2.652 2.652 0 0 0-1.456-1.851l-34.931-16.823a2.683 2.683 0 0 0-2.33 0l-34.931 16.823a2.684 2.684 0 0 0-1.449 1.823l-8.605 37.7a2.672 2.672 0 0 0 .494 2.357Z"
/>
</g>
</svg>
);
default: default:
return null; return null;
} }

View File

@ -38,6 +38,7 @@ export enum RepositoryKind {
Container, Container,
Kubewarden, Kubewarden,
Gatekeeper, Gatekeeper,
Kyverno,
} }
export interface SearchResults { export interface SearchResults {