mirror of https://github.com/artifacthub/hub.git
Add support for Krew index repositories (#1003)
Closes #619 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:
parent
fa34b01a5d
commit
7b9a68a827
|
|
@ -6,11 +6,11 @@
|
|||
[](https://artifacthub.io/packages/helm/artifact-hub/artifact-hub)
|
||||
[](https://gitpod.io/#https://github.com/artifacthub/hub)
|
||||
|
||||
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, Falco configurations, Open Policy Agent (OPA) policies, OLM operators and Tinkerbell actions.
|
||||
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, Falco configurations, Open Policy Agent (OPA) policies, OLM operators and Tinkerbell actions and kubectl plugins.
|
||||
|
||||
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.
|
||||
|
||||
The project, accessible at [https://artifacthub.io](https://artifacthub.io), is currently in development in a beta state. Support for Helm charts, Falco configurations, OPA policies, OLM operators and Tinkerbell actions is in development with plans to support more projects to follow. Pull requests, especially those to support other CNCF projects, are welcome. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) and [dev.md](./docs/dev.md) for more details.
|
||||
The project, accessible at [https://artifacthub.io](https://artifacthub.io), is currently in development in a beta state. Support for Helm charts, Falco configurations, OPA policies, OLM operators, Tinkerbell actions and kubectl plugins is in development with plans to support more projects to follow. Pull requests, especially those to support other CNCF projects, are welcome. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) and [dev.md](./docs/dev.md) for more details.
|
||||
|
||||
Feel free to ask any questions on the #artifact-hub channel in the CNCF Slack. To get an invite please visit [http://slack.cncf.io/](http://slack.cncf.io/).
|
||||
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ func (h *Handlers) setupRouter() {
|
|||
r.Route("/repositories", func(r chi.Router) {
|
||||
r.Use(h.Users.RequireLogin)
|
||||
r.Get("/", h.Repositories.GetAll)
|
||||
r.Get("/{kind:^helm$|^falco$|^olm$|^opa|^tbaction$}", h.Repositories.GetByKind)
|
||||
r.Get("/{kind:^helm$|^falco$|^olm$|^opa|^tbaction|^krew$}", h.Repositories.GetByKind)
|
||||
r.Route("/user", func(r chi.Router) {
|
||||
r.Get("/", h.Repositories.GetOwnedByUser)
|
||||
r.Post("/", h.Repositories.Add)
|
||||
|
|
@ -199,7 +199,7 @@ func (h *Handlers) setupRouter() {
|
|||
r.Get("/stats", h.Packages.GetStats)
|
||||
r.Get("/search", h.Packages.Search)
|
||||
r.With(h.Users.RequireLogin).Get("/starred", h.Packages.GetStarredByUser)
|
||||
r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction$}/{repoName}/{packageName}", func(r chi.Router) {
|
||||
r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew$}/{repoName}/{packageName}", func(r chi.Router) {
|
||||
r.Get("/feed/rss", h.Packages.RssFeed)
|
||||
r.Get("/{version}", h.Packages.Get)
|
||||
r.Get("/", h.Packages.Get)
|
||||
|
|
@ -332,7 +332,7 @@ func (h *Handlers) setupRouter() {
|
|||
|
||||
// Index special entry points
|
||||
r.Route("/packages", func(r chi.Router) {
|
||||
r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction$}/{repoName}/{packageName}", func(r chi.Router) {
|
||||
r.Route("/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew$}/{repoName}/{packageName}", func(r chi.Router) {
|
||||
r.With(h.Packages.InjectIndexMeta).Get("/{version}", h.Static.ServeIndex)
|
||||
r.With(h.Packages.InjectIndexMeta).Get("/", h.Static.ServeIndex)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -912,6 +912,17 @@ func TestBuildPackageURL(t *testing.T) {
|
|||
"2.0.0",
|
||||
baseURL + "/packages/tbaction/repo1/pkg1/2.0.0",
|
||||
},
|
||||
{
|
||||
&hub.Package{
|
||||
NormalizedName: "pkg1",
|
||||
Repository: &hub.Repository{
|
||||
Kind: hub.Krew,
|
||||
Name: "repo1",
|
||||
},
|
||||
},
|
||||
"2.0.0",
|
||||
baseURL + "/packages/krew/repo1/pkg1/2.0.0",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/artifacthub/hub/internal/tracker/falco"
|
||||
"github.com/artifacthub/hub/internal/tracker/generic"
|
||||
"github.com/artifacthub/hub/internal/tracker/helm"
|
||||
"github.com/artifacthub/hub/internal/tracker/krew"
|
||||
"github.com/artifacthub/hub/internal/tracker/olm"
|
||||
"github.com/artifacthub/hub/internal/util"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
|
@ -125,6 +126,8 @@ L:
|
|||
}
|
||||
case hub.Helm:
|
||||
t = helm.NewTracker(svc, r)
|
||||
case hub.Krew:
|
||||
t = krew.NewTracker(svc, r)
|
||||
case hub.OLM:
|
||||
t = olm.NewTracker(svc, r)
|
||||
case hub.OPA, hub.TBAction:
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ insert into repository_kind values (1, 'Falco rules');
|
|||
insert into repository_kind values (2, 'OPA policies');
|
||||
insert into repository_kind values (3, 'OLM operators');
|
||||
insert into repository_kind values (4, 'Tinkerbell actions');
|
||||
insert into repository_kind values (5, 'Krew kubectl plugins');
|
||||
|
||||
create table if not exists repository (
|
||||
repository_id uuid primary key default gen_random_uuid(),
|
||||
|
|
|
|||
|
|
@ -440,7 +440,8 @@ select results_eq(
|
|||
(1, 'Falco rules'),
|
||||
(2, 'OPA policies'),
|
||||
(3, 'OLM operators'),
|
||||
(4, 'Tinkerbell actions')
|
||||
(4, 'Tinkerbell actions'),
|
||||
(5, 'Krew kubectl plugins')
|
||||
$$,
|
||||
'Repository kinds should exist'
|
||||
);
|
||||
|
|
|
|||
|
|
@ -473,7 +473,7 @@ paths:
|
|||
$ref: "#/components/responses/TooManyRequests"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
"/repositories/{^helm$|^falco$|^opa$|^olm|^tbaction$}":
|
||||
"/repositories/{^helm$|^falco$|^opa$|^olm|^tbaction|^krew$}":
|
||||
get:
|
||||
tags:
|
||||
- Repositories
|
||||
|
|
@ -1067,6 +1067,27 @@ paths:
|
|||
$ref: "#/components/responses/TooManyRequests"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
"/packages/krew/{repoName}/{packageName}":
|
||||
get:
|
||||
tags:
|
||||
- Packages
|
||||
summary: Get package details
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/RepoNameParam"
|
||||
- $ref: "#/components/parameters/PackageNameParam"
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/KrewPluginsPackage"
|
||||
"404":
|
||||
$ref: "#/components/responses/NotFoundResponse"
|
||||
"429":
|
||||
$ref: "#/components/responses/TooManyRequests"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
"/packages/opa/{repoName}/{packageName}":
|
||||
get:
|
||||
tags:
|
||||
|
|
@ -1174,6 +1195,28 @@ paths:
|
|||
$ref: "#/components/responses/TooManyRequests"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
"/packages/krew/{repoName}/{packageName}/{version}":
|
||||
get:
|
||||
tags:
|
||||
- Packages
|
||||
summary: Get package details
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/RepoNameParam"
|
||||
- $ref: "#/components/parameters/PackageNameParam"
|
||||
- $ref: "#/components/parameters/VersionParam"
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/KrewPluginsPackage"
|
||||
"404":
|
||||
$ref: "#/components/responses/NotFoundResponse"
|
||||
"429":
|
||||
$ref: "#/components/responses/TooManyRequests"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
"/packages/opa/{repoName}/{packageName}/{version}":
|
||||
get:
|
||||
tags:
|
||||
|
|
@ -2173,6 +2216,8 @@ components:
|
|||
type: string
|
||||
nullable: false
|
||||
example: "http://repo.url"
|
||||
KrewPluginsPackage:
|
||||
$ref: "#/components/schemas/Package"
|
||||
Link:
|
||||
type: object
|
||||
nullable: false
|
||||
|
|
@ -2605,6 +2650,7 @@ components:
|
|||
- 2
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
description: |
|
||||
Repository kind:
|
||||
* `0` - Helm charts
|
||||
|
|
@ -2612,6 +2658,7 @@ components:
|
|||
* `2` - OPA policies
|
||||
* `3` - OLM operators
|
||||
* `4` - Tinkerbell actions
|
||||
* `5` - Krew kubectl plugins
|
||||
RepositoryKindParam:
|
||||
type: string
|
||||
enum:
|
||||
|
|
@ -2620,6 +2667,7 @@ components:
|
|||
- falco
|
||||
- olm
|
||||
- tbaction
|
||||
- krew
|
||||
description: |
|
||||
Repository kind name:
|
||||
* `helm` - Helm charts
|
||||
|
|
@ -2627,6 +2675,7 @@ components:
|
|||
* `opa` - OPA policies
|
||||
* `olm` - OLM operators
|
||||
* `tbaction` - Tinkerbell actions
|
||||
* `krew` - Krew kubectl plugins
|
||||
RepositorySummary:
|
||||
type: object
|
||||
required:
|
||||
|
|
@ -3069,7 +3118,7 @@ components:
|
|||
description: Package name
|
||||
RepoKindParam:
|
||||
in: path
|
||||
name: ^helm$|^falco$|^opa$|^olm|^tbaction$
|
||||
name: ^helm$|^falco$|^opa$|^olm|^tbaction|^krew$
|
||||
schema:
|
||||
$ref: "#/components/schemas/RepositoryKindParam"
|
||||
required: true
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
# Artifact Hub annotations in Krew plugin manifest file
|
||||
|
||||
Artifact Hub uses some data from the `plugin's manifest file` to populate the information for a package of kind Krew. Usually most of the information needed is already there, so there is no extra work required by maintainers to list their plugins on Artifact Hub.
|
||||
|
||||
However, sometimes there might be cases in which it may be useful to provide some more context that helps improving users' experience in Artifact Hub. This can be done using some special **annotations** in the [plugin's manifest](https://krew.sigs.k8s.io/docs/developer-guide/plugin-manifest/) file.
|
||||
|
||||
## Supported annotations
|
||||
|
||||
- **artifacthub.io/displayName** *(string)*
|
||||
|
||||
This annotations allows providing a nicely formatted name for the package.
|
||||
|
||||
- **artifacthub.io/keywords** *(yaml string, see example below)*
|
||||
|
||||
Use this annotation to provide a list of keywords that are relevant to your package. Keywords may improve the visibility of your package, making it appear in search results and in the related packages section. By default, the `kubernetes`, `kubectl` and `plugin` keywords are added to packages of Krew kind.
|
||||
|
||||
- **artifacthub.io/license** *(string)*
|
||||
|
||||
Use this annotation to indicate the plugin's license. It must be a valid [SPDX identifier](https://spdx.org/licenses/).
|
||||
|
||||
- **artifacthub.io/links** *(yaml string, see example below)*
|
||||
|
||||
This annotation allows including named links, which will be rendered nicely in Artifact Hub.
|
||||
|
||||
- **artifacthub.io/maintainers** *(yaml string, see example below)*
|
||||
|
||||
Use this annotation to list the maintainers of this package. Please note that the `email` field is *required* and entries that do not contain it will be silently ignored.
|
||||
|
||||
- **artifacthub.io/provider** *(string)*
|
||||
|
||||
Use this annotation to indicate the name of the organization or user providing this plugin. This may be useful for repositories where plugins are provided by a different entity than the one publishing them in Artifact Hub.
|
||||
|
||||
- **artifacthub.io/readme** *(string)*
|
||||
|
||||
Each package in Artifact Hub is expected to provide a `readme` file that is rendered in the package view of the UI. By default, the content of the `spec.description` field in the [plugin's manifest](https://krew.sigs.k8s.io/docs/developer-guide/plugin-manifest/) file is used as the readme file.
|
||||
|
||||
This annotation allows providing an Artifact Hub specific readme file in markdown format.
|
||||
|
||||
## Example
|
||||
|
||||
Artifact Hub annotations in `plugin manifest` file:
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
annotations:
|
||||
artifacthub.io/displayName: My plugin
|
||||
artifacthub.io/keywords: |
|
||||
- networking
|
||||
- security
|
||||
artifacthub.io/license: Apache-2.0
|
||||
artifacthub.io/links: |
|
||||
- name: link1
|
||||
url: https://link1.url
|
||||
- name: link2
|
||||
url: https://link2.url
|
||||
artifacthub.io/maintainers: |
|
||||
- name: user1
|
||||
email: user1@email.com
|
||||
- name: user2
|
||||
email: user2@email.com
|
||||
artifacthub.io/provider: Some organization
|
||||
artifacthub.io/readme: |
|
||||
## Package documentation in markdown format
|
||||
|
||||
Content added here will be displayed when the package's view in the UI.
|
||||
spec:
|
||||
...
|
||||
```
|
||||
|
|
@ -6,6 +6,7 @@ The following repositories kinds are supported at the moment:
|
|||
|
||||
- [Falco rules repositories](#falco-rules-repositories)
|
||||
- [Helm charts repositories](#helm-charts-repositories)
|
||||
- [Krew kubectl plugins repositories](#krew-kubectl-plugins-repositories)
|
||||
- [OLM operators repositories](#olm-operators-repositories)
|
||||
- [OPA policies repositories](#opa-policies-repositories)
|
||||
- [Tinkerbell actions repositories](#tinkerbell-actions-repositories)
|
||||
|
|
@ -106,6 +107,21 @@ Please note that there are some features that are not yet available for Helm rep
|
|||
|
||||
For additional information about Helm OCI support, please see the [HIP-0006](https://github.com/helm/community/blob/master/hips/hip-0006.md).
|
||||
|
||||
## Krew kubectl plugins repositories
|
||||
|
||||
Artifact Hub is able to process kubectl plugins listed in [Krew index repositories](https://krew.sigs.k8s.io/docs/developer-guide/custom-indexes/). Repositories are expected to be hosted in Github or Gitlab repos. When adding your repository to Artifact Hub, the url used **must** follow the following format:
|
||||
|
||||
- `https://github.com/user/repo`
|
||||
- `https://gitlab.com/user/repo`
|
||||
|
||||
By default the `master` branch is used, but it's possible to specify a different one from the UI.
|
||||
|
||||
For more information about the structure of the Krew index repository, please see the [Hosting Custom Plugin Indexes](https://krew.sigs.k8s.io/docs/developer-guide/custom-indexes/) official documentation.
|
||||
|
||||
Most of the metadata Artifact Hub needs is extracted from the [plugin's manifest](https://krew.sigs.k8s.io/docs/developer-guide/plugin-manifest/) file. However, there is some extra Artifact Hub specific metadata that you can set using some special annotations in the `plugin manifest` file. For more information, please see the [Artifact Hub Krew annotations documentation](https://github.com/artifacthub/hub/blob/master/docs/krew_annotations.md).
|
||||
|
||||
There is an extra metadata file that you can add to your repository named [artifacthub-repo.yml](https://github.com/artifacthub/hub/blob/master/docs/metadata/artifacthub-repo.yml), which can be used to setup features like [Verified Publisher](#verified-publisher) or [Ownership claim](#ownership-claim). This file must be located at the root of the repository.
|
||||
|
||||
## OLM operators repositories
|
||||
|
||||
OLM operators repositories are expected to be hosted in Github or Gitlab repos. When adding your repository to Artifact Hub, the url used **must** follow the following format:
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ Welcome to the [Artifact Hub](https://artifacthub.io/) documentation. Artifact H
|
|||
The documentation is organized in the following topics:
|
||||
|
||||
|
||||
|Topic|Description|
|
||||
|-|-|
|
||||
|[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.|
|
||||
|[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.|
|
||||
|[Packages security report](/docs/topics/security_report)|Explains how packages are scanned for security vulnerabilities and the structure of the security report.|
|
||||
|[Development environment setup](/docs/topics/dev)|This guide will help contributors setup their development environment to do some work on Artifact Hub.|
|
||||
|[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.|
|
||||
| Topic | Description |
|
||||
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [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. |
|
||||
| [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. |
|
||||
| [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. |
|
||||
| [Packages security report](/docs/topics/security_report) | Explains how packages are scanned for security vulnerabilities and the structure of the security report. |
|
||||
| [Development environment setup](/docs/topics/dev) | This guide will help contributors setup their development environment to do some work on Artifact Hub. |
|
||||
| [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. |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: "Krew"
|
||||
weight: 3
|
||||
aliases: [
|
||||
"/krew_annotations",
|
||||
]
|
||||
---
|
||||
4
go.mod
4
go.mod
|
|
@ -50,6 +50,10 @@ require (
|
|||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
||||
helm.sh/helm/v3 v3.4.2
|
||||
rsc.io/letsencrypt v0.0.3 // indirect
|
||||
sigs.k8s.io/krew v0.4.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
replace github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309
|
||||
|
||||
replace k8s.io/client-go => k8s.io/client-go v0.19.4
|
||||
|
|
|
|||
19
go.sum
19
go.sum
|
|
@ -275,6 +275,7 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
|
|||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/ekzhu/minhash-lsh v0.0.0-20171225071031-5c06ee8586a1 h1:/7G7q8SDJdrah5jDYqZI8pGFjSqiCzfSEO+NgqKCYX0=
|
||||
github.com/ekzhu/minhash-lsh v0.0.0-20171225071031-5c06ee8586a1/go.mod h1:yEtCVi+QamvzjEH4U/m6ZGkALIkF2xfQnFp0BcKmIOk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
|
|
@ -421,6 +422,7 @@ github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4
|
|||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
|
|
@ -515,6 +517,7 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
|
|
@ -707,6 +710,7 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
|
|
@ -971,6 +975,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
|||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94 h1:G04eS0JkAIVZfaJLjla9dNxkJCPiKIGZlw9AfOhzOD0=
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94/go.mod h1:b18R55ulyQ/h3RaWyloPyER7fWQVZvimKKhnI5OfrJQ=
|
||||
github.com/sahilm/fuzzy v0.0.5/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/satori/uuid v1.2.0 h1:6TFY4nxn5XwBx0gDfzbEMCNT6k4N/4FNIuN8RACZ0KI=
|
||||
|
|
@ -1309,6 +1314,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg=
|
||||
|
|
@ -1599,6 +1605,7 @@ gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
|
|||
gopkg.in/imdario/mergo.v0 v0.3.7 h1:QDotlIZtaO/p+Um0ok18HRTpq5i5/SAk/qprsor+9c8=
|
||||
gopkg.in/imdario/mergo.v0 v0.3.7/go.mod h1:9qPP6AGrlC1G2PTNXko614FwGZvorN7MiBU0Eppok+U=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
|
|
@ -1677,6 +1684,7 @@ k8s.io/apiextensions-apiserver v0.19.3 h1:WZxBypSHW4SdXHbdPTS/Jy7L2la6Niggs8BuU5
|
|||
k8s.io/apiextensions-apiserver v0.19.3/go.mod h1:igVEkrE9TzInc1tYE7qSqxaLg/rEAp6B5+k9Q7+IC8Q=
|
||||
k8s.io/apiextensions-apiserver v0.19.4 h1:D9ak9T012tb3vcGFWYmbQuj9SCC8YM4zhA4XZqsAQC4=
|
||||
k8s.io/apiextensions-apiserver v0.19.4/go.mod h1:B9rpH/nu4JBCtuUp3zTTk8DEjZUupZTBEec7/2zNRYw=
|
||||
k8s.io/apimachinery v0.0.0-20190717022731-0bb8574e0887/go.mod h1:sBJWIJZfxLhp7mRsRyuAE/NfKTr3kXGR1iaqg8O0gJo=
|
||||
k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA=
|
||||
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/apimachinery v0.18.8 h1:jimPrycCqgx2QPearX3to1JePz7wSbVLq+7PdBTTwQ0=
|
||||
|
|
@ -1695,12 +1703,6 @@ k8s.io/apiserver v0.19.4 h1:X40UuyVt6DcYWIh2olcePkyKO0LRJFvxWC0kLxYvkZU=
|
|||
k8s.io/apiserver v0.19.4/go.mod h1:X8WRHCR1UGZDd7HpV0QDc1h/6VbbpAeAGyxSh8yzZXw=
|
||||
k8s.io/cli-runtime v0.19.4 h1:FPpoqFbWsFzRbZNRI+o/+iiLFmWMYTmBueIj3OaNVTI=
|
||||
k8s.io/cli-runtime v0.19.4/go.mod h1:m8G32dVbKOeaX1foGhleLEvNd6REvU7YnZyWn5//9rw=
|
||||
k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE=
|
||||
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
|
||||
k8s.io/client-go v0.18.8 h1:SdbLpIxk5j5YbFr1b7fq8S7mDgDjYmUxSbszyoesoDM=
|
||||
k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU=
|
||||
k8s.io/client-go v0.19.3 h1:ctqR1nQ52NUs6LpI0w+a5U+xjYwflFwA13OJKcicMxg=
|
||||
k8s.io/client-go v0.19.3/go.mod h1:+eEMktZM+MG0KO+PTkci8xnbCZHvj9TqR6Q1XDUIJOM=
|
||||
k8s.io/client-go v0.19.4 h1:85D3mDNoLF+xqpyE9Dh/OtrJDyJrSRKkHmDXIbEzer8=
|
||||
k8s.io/client-go v0.19.4/go.mod h1:ZrEy7+wj9PjH5VMBCuu/BDlvtUAku0oVFk4MmnW9mWA=
|
||||
k8s.io/cloud-provider v0.18.8/go.mod h1:cn9AlzMPVIXA4HHLVbgGUigaQlZyHSZ7WAwDEFNrQSs=
|
||||
|
|
@ -1724,11 +1726,13 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
|||
k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
|
||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM=
|
||||
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
|
|
@ -1737,6 +1741,7 @@ k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C
|
|||
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ=
|
||||
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
|
||||
k8s.io/kubectl v0.19.4/go.mod h1:XPmlu4DJEYgD83pvZFeKF8+MSvGnYGqunbFSrJsqHv0=
|
||||
k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/legacy-cloud-providers v0.18.8/go.mod h1:tgp4xYf6lvjrWnjQwTOPvWQE9IVqSBGPF4on0IyICQE=
|
||||
k8s.io/metrics v0.19.4/go.mod h1:a0gvAzrxQPw2ouBqnXI7X9qlggpPkKAFgWU/Py+KZiU=
|
||||
|
|
@ -1762,6 +1767,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQb
|
|||
sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo=
|
||||
sigs.k8s.io/controller-tools v0.3.0 h1:y3YD99XOyWaXkiF1kd41uRvfp/64teWcrEZFuHxPhJ4=
|
||||
sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
|
||||
sigs.k8s.io/krew v0.4.0 h1:Y9qeOcShVUKD0IAAhOwKuwivWkcTfxljZsdlJS8ATCQ=
|
||||
sigs.k8s.io/krew v0.4.0/go.mod h1:e2cG2GilCwX2JjmeVblZacw7aUyHzIlL27uclbwJY98=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ const (
|
|||
|
||||
// TBAction represents a repository with Tinkerbell actions.
|
||||
TBAction RepositoryKind = 4
|
||||
|
||||
// Krew represents a repository with kubectl plugins that can be managed by
|
||||
// the Krew plugin manager.
|
||||
Krew RepositoryKind = 5
|
||||
)
|
||||
|
||||
// GetKindName returns the name of the provided repository kind.
|
||||
|
|
@ -44,6 +48,8 @@ func GetKindName(kind RepositoryKind) string {
|
|||
return "falco"
|
||||
case Helm:
|
||||
return "helm"
|
||||
case Krew:
|
||||
return "krew"
|
||||
case OLM:
|
||||
return "olm"
|
||||
case OPA:
|
||||
|
|
@ -63,6 +69,8 @@ func GetKindFromName(kind string) (RepositoryKind, error) {
|
|||
return Falco, nil
|
||||
case "helm":
|
||||
return Helm, nil
|
||||
case "krew":
|
||||
return Krew, nil
|
||||
case "olm":
|
||||
return OLM, nil
|
||||
case "opa":
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ func (c *Cloner) CloneRepository(ctx context.Context, r *hub.Repository) (string
|
|||
// Parse repository url
|
||||
var repoBaseURL, packagesPath string
|
||||
switch r.Kind {
|
||||
case hub.Falco, hub.OLM, hub.OPA, hub.TBAction:
|
||||
case hub.Falco, hub.Krew, hub.OLM, hub.OPA, hub.TBAction:
|
||||
matches := GitRepoURLRE.FindStringSubmatch(r.URL)
|
||||
if len(matches) < 2 {
|
||||
return "", "", fmt.Errorf("invalid repository url")
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ func (m *Manager) ClaimOwnership(ctx context.Context, repoName, orgName string)
|
|||
u, _ := url.Parse(r.URL)
|
||||
u.Path = path.Join(u.Path, hub.RepositoryMetadataFile)
|
||||
mdFile = u.String()
|
||||
case hub.Falco, hub.OLM, hub.OPA, hub.TBAction:
|
||||
case hub.Falco, hub.Krew, hub.OLM, hub.OPA, hub.TBAction:
|
||||
tmpDir, packagesPath, err := m.rc.CloneRepository(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -660,7 +660,7 @@ func (m *Manager) validateURL(r *hub.Repository) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
case hub.Falco, hub.OLM, hub.OPA, hub.TBAction:
|
||||
case hub.Falco, hub.Krew, hub.OLM, hub.OPA, hub.TBAction:
|
||||
if SchemeIsHTTP(u) && !GitRepoURLRE.MatchString(r.URL) {
|
||||
return errors.New("invalid url format")
|
||||
}
|
||||
|
|
@ -702,6 +702,7 @@ func isValidKind(kind hub.RepositoryKind) bool {
|
|||
for _, validKind := range []hub.RepositoryKind{
|
||||
hub.Falco,
|
||||
hub.Helm,
|
||||
hub.Krew,
|
||||
hub.OLM,
|
||||
hub.OPA,
|
||||
hub.TBAction,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
"{
|
||||
invalid
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
apiVersion: krew.googlecontainertools.github.com/v1alpha2
|
||||
kind: Plugin
|
||||
metadata:
|
||||
name: test-plugin
|
||||
annotations:
|
||||
artifacthub.io/displayName: My test plugin
|
||||
artifacthub.io/keywords: |
|
||||
- networking
|
||||
- security
|
||||
artifacthub.io/license: Apache-2.0
|
||||
artifacthub.io/links: |
|
||||
- name: link1
|
||||
url: https://link1.url
|
||||
- name: link2
|
||||
url: https://link2.url
|
||||
artifacthub.io/maintainers: |
|
||||
- name: user1
|
||||
email: user1@email.com
|
||||
- name: user2
|
||||
email: user2@email.com
|
||||
artifacthub.io/provider: Some organization
|
||||
artifacthub.io/readme: |
|
||||
spec:
|
||||
version: v0.1.0
|
||||
shortDescription: Test plugin
|
||||
homepage: https://test/plugin
|
||||
description: This is just a test plugin
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
package krew
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/artifacthub/hub/internal/hub"
|
||||
"github.com/artifacthub/hub/internal/repo"
|
||||
"github.com/artifacthub/hub/internal/tracker"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"sigs.k8s.io/krew/pkg/index"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
const (
|
||||
displayNameAnnotation = "artifacthub.io/displayName"
|
||||
keywordsAnnotation = "artifacthub.io/keywords"
|
||||
licenseAnnotation = "artifacthub.io/license"
|
||||
linksAnnotation = "artifacthub.io/links"
|
||||
maintainersAnnotation = "artifacthub.io/maintainers"
|
||||
providerAnnotation = "artifacthub.io/provider"
|
||||
readmeAnnotation = "artifacthub.io/readme"
|
||||
)
|
||||
|
||||
// Tracker is in charge of tracking the packages available in repository,
|
||||
// registering and unregistering them as needed.
|
||||
type Tracker struct {
|
||||
svc *tracker.Services
|
||||
r *hub.Repository
|
||||
logger zerolog.Logger
|
||||
}
|
||||
|
||||
// NewTracker creates a new Tracker instance.
|
||||
func NewTracker(
|
||||
svc *tracker.Services,
|
||||
r *hub.Repository,
|
||||
opts ...func(t tracker.Tracker),
|
||||
) tracker.Tracker {
|
||||
t := &Tracker{
|
||||
svc: svc,
|
||||
r: r,
|
||||
logger: log.With().Str("repo", r.Name).Str("kind", hub.GetKindName(r.Kind)).Logger(),
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(t)
|
||||
}
|
||||
if t.svc.Rc == nil {
|
||||
t.svc.Rc = &repo.Cloner{}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Track registers or unregisters the packages available as needed.
|
||||
func (t *Tracker) Track() error {
|
||||
// Clone repository
|
||||
t.logger.Debug().Msg("cloning repository")
|
||||
tmpDir, packagesPath, err := t.svc.Rc.CloneRepository(t.svc.Ctx, t.r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error cloning repository: %w", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Get repository metadata
|
||||
var rmd *hub.RepositoryMetadata
|
||||
rmd, _ = t.svc.Rm.GetMetadata(filepath.Join(tmpDir, hub.RepositoryMetadataFile))
|
||||
|
||||
// Load packages already registered from this repository
|
||||
packagesRegistered, err := t.svc.Rm.GetPackagesDigest(t.svc.Ctx, t.r.RepositoryID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting registered packages: %w", err)
|
||||
}
|
||||
|
||||
// Register available packages when needed
|
||||
bypassDigestCheck := t.svc.Cfg.GetBool("tracker.bypassDigestCheck")
|
||||
packagesAvailable := make(map[string]struct{})
|
||||
pluginsPath := filepath.Join(tmpDir, packagesPath, "plugins")
|
||||
pluginManifestFiles, err := ioutil.ReadDir(pluginsPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading plugins directory: %w", err)
|
||||
}
|
||||
for _, file := range pluginManifestFiles {
|
||||
// Return ASAP if context is cancelled
|
||||
select {
|
||||
case <-t.svc.Ctx.Done():
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
// Only process plugins files
|
||||
if !file.Mode().IsRegular() || filepath.Ext(file.Name()) != ".yaml" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Read and parse package manifest
|
||||
data, err := ioutil.ReadFile(filepath.Join(pluginsPath, file.Name()))
|
||||
if err != nil {
|
||||
t.warn(fmt.Errorf("error reading package manifest file: %w", err))
|
||||
continue
|
||||
}
|
||||
var manifest *index.Plugin
|
||||
if err = yaml.Unmarshal(data, &manifest); err != nil || manifest == nil {
|
||||
t.warn(fmt.Errorf("error unmarshaling package manifest file: %w", err))
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract package name and version from manifest
|
||||
name := manifest.ObjectMeta.Name
|
||||
sv, err := semver.NewVersion(manifest.Spec.Version)
|
||||
if err != nil {
|
||||
t.warn(fmt.Errorf("invalid package (%s) version (%s): %w", name, manifest.Spec.Version, err))
|
||||
continue
|
||||
}
|
||||
version := sv.String()
|
||||
|
||||
// Check if this package version is already registered
|
||||
key := fmt.Sprintf("%s@%s", name, version)
|
||||
packagesAvailable[key] = struct{}{}
|
||||
if _, ok := packagesRegistered[key]; ok && !bypassDigestCheck {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if this package should be ignored
|
||||
if tracker.ShouldIgnorePackage(rmd, name, version) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Register package version
|
||||
t.logger.Debug().Str("name", name).Str("v", version).Msg("registering package")
|
||||
err = t.registerPackage(name, version, manifest)
|
||||
if err != nil {
|
||||
t.warn(fmt.Errorf("error registering package %s version %s: %w", name, version, err))
|
||||
}
|
||||
}
|
||||
|
||||
// Unregister packages not available anymore
|
||||
if len(packagesAvailable) > 0 {
|
||||
for key := range packagesRegistered {
|
||||
// Return ASAP if context is cancelled
|
||||
select {
|
||||
case <-t.svc.Ctx.Done():
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
// Extract package name and version from key
|
||||
p := strings.Split(key, "@")
|
||||
name := p[0]
|
||||
version := p[1]
|
||||
|
||||
// Unregister pkg if it's not available anymore or if it's ignored
|
||||
_, ok := packagesAvailable[key]
|
||||
if !ok || tracker.ShouldIgnorePackage(rmd, name, version) {
|
||||
t.logger.Debug().Str("name", name).Str("v", version).Msg("unregistering package")
|
||||
if err := t.unregisterPackage(name, version); err != nil {
|
||||
t.warn(fmt.Errorf("error unregistering package %s version %s: %w", name, version, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set verified publisher flag
|
||||
if err := tracker.SetVerifiedPublisherFlag(t.svc.Ctx, t.svc.Rm, t.r, rmd); err != nil {
|
||||
t.warn(fmt.Errorf("error setting verified publisher flag: %w", err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// registerPackage registers a package version using the package manifest
|
||||
// provided.
|
||||
func (t *Tracker) registerPackage(name, version string, manifest *index.Plugin) error {
|
||||
// Prepare package to be registered
|
||||
p := &hub.Package{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Description: manifest.Spec.ShortDescription,
|
||||
HomeURL: manifest.Spec.Homepage,
|
||||
Readme: manifest.Spec.Description,
|
||||
Repository: t.r,
|
||||
}
|
||||
|
||||
// Enrich package with information from annotations
|
||||
if err := enrichPackageFromAnnotations(p, manifest.Annotations); err != nil {
|
||||
return fmt.Errorf("error enriching package %s version %s: %w", name, version, err)
|
||||
}
|
||||
|
||||
// Register package
|
||||
return t.svc.Pm.Register(t.svc.Ctx, p)
|
||||
}
|
||||
|
||||
// unregisterPackage unregisters the package version provided.
|
||||
func (t *Tracker) unregisterPackage(name, version string) error {
|
||||
p := &hub.Package{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Repository: t.r,
|
||||
}
|
||||
return t.svc.Pm.Unregister(t.svc.Ctx, p)
|
||||
}
|
||||
|
||||
// warn is a helper that sends the error provided to the errors collector and
|
||||
// logs it as a warning.
|
||||
func (t *Tracker) warn(err error) {
|
||||
t.svc.Ec.Append(t.r.RepositoryID, err)
|
||||
t.logger.Warn().Err(err).Send()
|
||||
}
|
||||
|
||||
// enrichPackageFromAnnotations adds some extra information to the package from
|
||||
// the provided annotations.
|
||||
func enrichPackageFromAnnotations(p *hub.Package, annotations map[string]string) error {
|
||||
// Display name
|
||||
p.DisplayName = annotations[displayNameAnnotation]
|
||||
|
||||
// Keywords
|
||||
p.Keywords = []string{
|
||||
"kubernetes",
|
||||
"kubectl",
|
||||
"plugin",
|
||||
}
|
||||
if v, ok := annotations[keywordsAnnotation]; ok {
|
||||
var extraKeywords []string
|
||||
if err := yaml.Unmarshal([]byte(v), &extraKeywords); err != nil {
|
||||
return fmt.Errorf("invalid keywords value: %s", v)
|
||||
}
|
||||
p.Keywords = append(p.Keywords, extraKeywords...)
|
||||
}
|
||||
|
||||
// License
|
||||
p.License = annotations[licenseAnnotation]
|
||||
|
||||
// Links
|
||||
if v, ok := annotations[linksAnnotation]; ok {
|
||||
var links []*hub.Link
|
||||
if err := yaml.Unmarshal([]byte(v), &links); err != nil {
|
||||
return fmt.Errorf("invalid links value: %s", v)
|
||||
}
|
||||
p.Links = links
|
||||
}
|
||||
|
||||
// Maintainers
|
||||
if v, ok := annotations[maintainersAnnotation]; ok {
|
||||
var maintainers []*hub.Maintainer
|
||||
if err := yaml.Unmarshal([]byte(v), &maintainers); err != nil {
|
||||
return fmt.Errorf("invalid maintainers value: %s", v)
|
||||
}
|
||||
p.Maintainers = maintainers
|
||||
}
|
||||
|
||||
// Provider
|
||||
p.Provider = annotations[providerAnnotation]
|
||||
|
||||
// Readme
|
||||
if v, ok := annotations[readmeAnnotation]; ok && v != "" {
|
||||
p.Readme = v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,347 @@
|
|||
package krew
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/artifacthub/hub/internal/hub"
|
||||
"github.com/artifacthub/hub/internal/pkg"
|
||||
"github.com/artifacthub/hub/internal/repo"
|
||||
"github.com/artifacthub/hub/internal/tests"
|
||||
"github.com/artifacthub/hub/internal/tracker"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
zerolog.SetGlobalLevel(zerolog.Disabled)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestTracker(t *testing.T) {
|
||||
r := &hub.Repository{
|
||||
Kind: hub.Krew,
|
||||
RepositoryID: "00000000-0000-0000-0000-000000000001",
|
||||
Name: "repo1",
|
||||
URL: "https://github.com/org1/repo1",
|
||||
VerifiedPublisher: false,
|
||||
}
|
||||
|
||||
t.Run("error cloning repository", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return("", "", tests.ErrFake)
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.True(t, errors.Is(err, tests.ErrFake))
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("error loading repository registered packages", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return("", "", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(nil, tests.ErrFake)
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.True(t, errors.Is(err, tests.ErrFake))
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("no packages in path, nothing to do", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return(".", "testdata/path1", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(nil, nil)
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.NoError(t, err)
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("error parsing package manifest file", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return(".", "testdata/path2", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(nil, nil)
|
||||
tw.ec.On("Append", r.RepositoryID, mock.Anything).Return()
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.NoError(t, err)
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("error registering package version", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return(".", "testdata/path3", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(nil, nil)
|
||||
tw.pm.On("Register", tw.ctx, mock.Anything).Return(tests.ErrFake)
|
||||
tw.ec.On("Append", r.RepositoryID, mock.Anything).Return()
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.NoError(t, err)
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("no need to register package version because it is already registered", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return(".", "testdata/path3", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(map[string]string{
|
||||
"test-plugin@0.1.0": "",
|
||||
}, nil)
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.NoError(t, err)
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("package version not registered because it is ignored", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return(".", "testdata/path3", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{
|
||||
Ignore: []*hub.RepositoryIgnoreEntry{
|
||||
{
|
||||
Name: "test-plugin",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(nil, nil)
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.NoError(t, err)
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("package version registered successfully", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return(".", "testdata/path3", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(nil, nil)
|
||||
tw.pm.On("Register", tw.ctx, &hub.Package{
|
||||
Name: "test-plugin",
|
||||
DisplayName: "My test plugin",
|
||||
Description: "Test plugin",
|
||||
HomeURL: "https://test/plugin",
|
||||
Keywords: []string{"kubernetes", "kubectl", "plugin", "networking", "security"},
|
||||
Readme: "This is just a test plugin",
|
||||
Version: "0.1.0",
|
||||
Provider: "Some organization",
|
||||
Repository: r,
|
||||
License: "Apache-2.0",
|
||||
Links: []*hub.Link{
|
||||
{
|
||||
Name: "link1",
|
||||
URL: "https://link1.url",
|
||||
},
|
||||
{
|
||||
Name: "link2",
|
||||
URL: "https://link2.url",
|
||||
},
|
||||
},
|
||||
Maintainers: []*hub.Maintainer{
|
||||
{
|
||||
Name: "user1",
|
||||
Email: "user1@email.com",
|
||||
},
|
||||
{
|
||||
Name: "user2",
|
||||
Email: "user2@email.com",
|
||||
},
|
||||
},
|
||||
}).Return(nil)
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.NoError(t, err)
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("error unregistering package version", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return(".", "testdata/path3", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{RepositoryID: r.RepositoryID}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(map[string]string{
|
||||
"test-plugin@0.1.0": "",
|
||||
"test-plugin@0.2.0": "",
|
||||
}, nil)
|
||||
tw.rm.On("SetVerifiedPublisher", tw.ctx, r.RepositoryID, true).Return(nil)
|
||||
tw.pm.On("Unregister", tw.ctx, &hub.Package{
|
||||
Name: "test-plugin",
|
||||
Version: "0.2.0",
|
||||
Repository: r,
|
||||
}).Return(tests.ErrFake)
|
||||
tw.ec.On("Append", r.RepositoryID, mock.Anything).Return()
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.NoError(t, err)
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("no packages unregistered because there are no packages available", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return(".", "testdata/path1", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{RepositoryID: r.RepositoryID}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(map[string]string{
|
||||
"test-plugin@0.1.0": "",
|
||||
}, nil)
|
||||
tw.rm.On("SetVerifiedPublisher", tw.ctx, r.RepositoryID, true).Return(nil)
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.NoError(t, err)
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("package version unregistered successfully", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return(".", "testdata/path3", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{RepositoryID: r.RepositoryID}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(map[string]string{
|
||||
"test-plugin@0.1.0": "",
|
||||
"test-plugin@0.2.0": "",
|
||||
}, nil)
|
||||
tw.rm.On("SetVerifiedPublisher", tw.ctx, r.RepositoryID, true).Return(nil)
|
||||
tw.pm.On("Unregister", tw.ctx, &hub.Package{
|
||||
Name: "test-plugin",
|
||||
Version: "0.2.0",
|
||||
Repository: r,
|
||||
}).Return(nil)
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.NoError(t, err)
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("all package versions unregistered because package is ignored", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup tracker and expectations
|
||||
tw := newTrackerWrapper(r)
|
||||
tw.rc.On("CloneRepository", tw.ctx, r).Return(".", "testdata/path3", nil)
|
||||
tw.rm.On("GetMetadata", mock.Anything).Return(&hub.RepositoryMetadata{
|
||||
RepositoryID: r.RepositoryID,
|
||||
Ignore: []*hub.RepositoryIgnoreEntry{
|
||||
{
|
||||
Name: "test-plugin",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
tw.rm.On("GetPackagesDigest", tw.ctx, r.RepositoryID).Return(map[string]string{
|
||||
"test-plugin@0.1.0": "",
|
||||
}, nil)
|
||||
tw.rm.On("SetVerifiedPublisher", tw.ctx, r.RepositoryID, true).Return(nil)
|
||||
tw.pm.On("Unregister", tw.ctx, &hub.Package{
|
||||
Name: "test-plugin",
|
||||
Version: "0.1.0",
|
||||
Repository: r,
|
||||
}).Return(nil)
|
||||
|
||||
// Run tracker and check expectations
|
||||
err := tw.t.Track()
|
||||
assert.NoError(t, err)
|
||||
tw.assertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func withRepositoryCloner(rc hub.RepositoryCloner) func(t tracker.Tracker) {
|
||||
return func(t tracker.Tracker) {
|
||||
t.(*Tracker).svc.Rc = rc
|
||||
}
|
||||
}
|
||||
|
||||
type trackerWrapper struct {
|
||||
ctx context.Context
|
||||
cfg *viper.Viper
|
||||
rc *repo.ClonerMock
|
||||
rm *repo.ManagerMock
|
||||
pm *pkg.ManagerMock
|
||||
re *repo.OLMRepositoryExporterMock
|
||||
ec *tracker.ErrorsCollectorMock
|
||||
t tracker.Tracker
|
||||
}
|
||||
|
||||
func newTrackerWrapper(r *hub.Repository) *trackerWrapper {
|
||||
ctx := context.Background()
|
||||
cfg := viper.New()
|
||||
rc := &repo.ClonerMock{}
|
||||
rm := &repo.ManagerMock{}
|
||||
pm := &pkg.ManagerMock{}
|
||||
re := &repo.OLMRepositoryExporterMock{}
|
||||
ec := &tracker.ErrorsCollectorMock{}
|
||||
svc := &tracker.Services{
|
||||
Ctx: ctx,
|
||||
Cfg: cfg,
|
||||
Rm: rm,
|
||||
Pm: pm,
|
||||
Re: re,
|
||||
Ec: ec,
|
||||
}
|
||||
|
||||
t := NewTracker(svc, r, withRepositoryCloner(rc))
|
||||
|
||||
return &trackerWrapper{
|
||||
ctx: ctx,
|
||||
cfg: cfg,
|
||||
rc: rc,
|
||||
rm: rm,
|
||||
pm: pm,
|
||||
re: re,
|
||||
ec: ec,
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (tw *trackerWrapper) assertExpectations(t *testing.T) {
|
||||
tw.re.AssertExpectations(t)
|
||||
tw.rc.AssertExpectations(t)
|
||||
tw.rm.AssertExpectations(t)
|
||||
tw.pm.AssertExpectations(t)
|
||||
tw.ec.AssertExpectations(t)
|
||||
}
|
||||
|
|
@ -6,3 +6,4 @@ cat docs/www/headers/repositories docs/repositories.md > docs/www/content/topics
|
|||
cat docs/www/headers/security_report docs/security_report.md > docs/www/content/topics/security_report.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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="480" height="480">
|
||||
<desc iVinci="yes" version="4.6" gridStep="20" showGrid="no" snapToGrid="no" codePlatform="0"/>
|
||||
<g id="Layer1" name="Layer 1" opacity="1">
|
||||
<g id="Shape1">
|
||||
<desc shapeID="1" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-7.8775,-25.8965,15.755,51.793)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,460.034,240.683)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath1" d="M467.709,233.933 C466.842,246.638 461.706,257.282 452.358,265.915 C452.408,249.096 452.447,232.268 452.499,215.451 C459.226,220.24 464.113,226.562 467.709,233.933 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape2">
|
||||
<desc shapeID="2" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-8.248,-27.1165,16.496,54.233)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,23.7502,173.554)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath2" d="M15.7139,180.619 C16.6181,167.315 21.9993,156.169 31.7865,147.134 C31.7339,164.743 31.6949,182.364 31.6423,199.975 C24.5949,194.955 19.4807,188.336 15.7139,180.619 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape3">
|
||||
<desc shapeID="3" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-9.862,-32.372,19.724,64.744)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,23.3955,306.024)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath3" d="M13.7867,297.588 C14.8818,313.486 21.3153,326.779 33.0044,337.565 C32.9313,316.544 32.8602,295.496 32.7696,274.483 C24.3738,280.479 18.2559,288.371 13.7867,297.588 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape4">
|
||||
<desc shapeID="4" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-108.885,-181.865,217.771,363.731)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,136.553,298.618)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath4" d="M241.749,475.729 C233.727,476.301 226.348,473.995 219.494,470.071 C165.127,438.967 110.782,407.815 56.4703,376.613 C39.8316,367.045 31.3773,352.37 31.276,332.953 C30.9466,271.043 30.7342,209.134 30.4634,147.225 C30.4224,137.972 32.4179,129.296 37.3422,121.42 C38.0641,121.771 38.8056,122.083 39.5052,122.475 C91.7031,151.917 143.91,181.347 196.089,210.824 C211.02,219.255 225.875,227.826 240.762,236.339 C241.017,236.672 241.272,237.016 241.516,237.349 C241.571,238.662 241.673,239.976 241.678,241.291 C242.002,317.742 242.323,394.199 242.644,470.658 C242.649,471.968 242.558,473.277 242.52,474.596 C242.263,474.962 242.006,475.35 241.749,475.729 Z" style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape5">
|
||||
<desc shapeID="5" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-108.318,-182.376,216.635,364.752)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,347.312,298.584)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath5" d="M241.779,475.041 C241.823,473.722 241.918,472.411 241.918,471.102 C241.92,394.643 241.92,318.187 241.918,241.734 C241.918,240.419 241.823,239.105 241.775,237.792 C241.92,237.512 241.987,237.213 241.972,236.898 C309.884,198.226 377.793,159.56 445.696,120.889 C450.769,128.704 452.744,137.478 452.76,146.652 C452.849,208.728 452.917,270.806 452.736,332.88 C452.681,352.023 444.171,366.698 427.764,376.214 C373.882,407.507 319.941,438.696 266.003,469.888 C258.582,474.178 250.698,476.87 241.988,476.168 C241.917,475.79 241.84,475.406 241.779,475.041 Z" style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape6">
|
||||
<desc shapeID="6" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-209.173,-120.03,418.345,240.06)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,241.891,119.949)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath6" d="M445.695,120.889 C377.793,159.56 309.883,198.227 241.971,236.898 L241.506,236.768 L241.023,236.777 C226.17,228.203 211.351,219.569 196.457,211.075 C144.401,181.379 92.3218,151.73 40.2466,122.068 C39.549,121.673 38.8075,121.358 38.0875,121.005 C42.2255,113.259 48.4564,107.682 55.8778,103.382 C109.44,72.3605 163.029,41.3904 216.569,10.3335 C233.68,0.406021 250.641,0.650581 267.64,10.503 C320.183,40.949 372.725,71.4076 425.3,101.808 C433.575,106.593 440.976,112.225 445.695,120.889 Z" style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape7">
|
||||
<desc shapeID="7" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(0,-122.427,0,244.854)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,241.774,356.73)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath7" d="M241.774,237.446 L241.774,476.015 " style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill-rule:nonzero;fill:#cc9966;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape8">
|
||||
<desc shapeID="8" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-104.647,-59.991,209.295,119.982)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,343.335,178.995)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath8" d="M241.373,237.446 L445.296,120.543 " style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape9">
|
||||
<desc shapeID="9" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-104.317,-59.7325,208.634,119.465)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,140.134,179.246)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath9" d="M241.774,237.446 L38.4948,121.047 " style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape10">
|
||||
<desc shapeID="10" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-116.719,-50.1968,233.439,100.394)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,123.61,131.711)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath10" d="M31.8713,101.652 C75.5019,65.6035 114.27,88.3308 153.403,111.242 C176.076,124.503 197.556,141.171 225.788,141.12 C229.632,141.109 233.478,140.388 237.334,139.99 C233.446,142.396 229.792,145.262 225.668,147.144 C195.444,160.917 166.959,154.378 138.428,140.68 C102.014,123.184 65.7293,118.657 31.7875,147.135 C22.0003,156.17 16.6191,167.316 15.7149,180.62 C9.33591,166.437 8.39471,151.839 11.9082,136.747 C15.1079,123.026 22.6044,111.857 31.8713,101.652 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape11">
|
||||
<desc shapeID="11" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-139.358,-59.8922,278.715,119.784)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,142.781,355.944)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath11" d="M33.2938,391.855 C85.4323,434.83 131.691,407.666 178.395,380.273 C205.453,364.4 231.095,344.483 264.77,344.509 C269.365,344.524 273.956,345.378 278.562,345.848 C273.909,342.986 269.542,339.577 264.627,337.339 C228.526,320.919 194.529,328.747 160.482,345.148 C117.025,366.063 73.7121,371.506 33.1457,337.566 C21.4566,326.78 15.0231,313.487 13.9279,297.589 C6.32814,314.527 5.22034,331.964 9.43433,349.968 C13.2751,366.341 22.239,379.672 33.2938,391.855 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape12">
|
||||
<desc shapeID="12" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-111.465,-47.9396,222.93,95.8792)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,364.67,280.641)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath12" d="M452.278,309.349 C410.61,343.775 373.587,322.074 336.208,300.19 C314.559,287.527 294.044,271.609 267.091,271.658 C263.414,271.668 259.741,272.358 256.066,272.734 C259.768,270.44 263.266,267.703 267.203,265.905 C296.064,252.752 323.269,258.995 350.517,272.075 C385.294,288.784 419.944,293.107 452.359,265.914 C461.707,257.281 466.843,246.637 467.71,233.932 C473.798,247.479 474.7,261.419 471.343,275.831 C468.287,288.933 461.126,299.599 452.278,309.349 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
|
|
@ -0,0 +1,158 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="480" height="480">
|
||||
<desc iVinci="yes" version="4.6" gridStep="20" showGrid="no" snapToGrid="no" codePlatform="0"/>
|
||||
<g id="Layer1" name="Layer 1" opacity="1">
|
||||
<g id="Shape1">
|
||||
<desc shapeID="1" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-7.8775,-25.8965,15.755,51.793)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,460.034,240.683)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath1" d="M467.709,233.933 C466.842,246.638 461.706,257.282 452.358,265.915 C452.408,249.096 452.447,232.268 452.499,215.451 C459.226,220.24 464.113,226.562 467.709,233.933 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape2">
|
||||
<desc shapeID="2" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-8.248,-27.1165,16.496,54.233)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,23.7502,173.554)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath2" d="M15.7139,180.619 C16.6181,167.315 21.9993,156.169 31.7865,147.134 C31.7339,164.743 31.6949,182.364 31.6423,199.975 C24.5949,194.955 19.4807,188.336 15.7139,180.619 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape3">
|
||||
<desc shapeID="3" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-9.862,-32.372,19.724,64.744)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,23.3955,306.024)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath3" d="M13.7867,297.588 C14.8818,313.486 21.3153,326.779 33.0044,337.565 C32.9313,316.544 32.8602,295.496 32.7696,274.483 C24.3738,280.479 18.2559,288.371 13.7867,297.588 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape4">
|
||||
<desc shapeID="4" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-108.885,-181.865,217.771,363.731)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,136.553,298.618)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath4" d="M241.749,475.729 C233.727,476.301 226.348,473.995 219.494,470.071 C165.127,438.967 110.782,407.815 56.4703,376.613 C39.8316,367.045 31.3773,352.37 31.276,332.953 C30.9466,271.043 30.7342,209.134 30.4634,147.225 C30.4224,137.972 32.4179,129.296 37.3422,121.42 C38.0641,121.771 38.8056,122.083 39.5052,122.475 C91.7031,151.917 143.91,181.347 196.089,210.824 C211.02,219.255 225.875,227.826 240.762,236.339 C241.017,236.672 241.272,237.016 241.516,237.349 C241.571,238.662 241.673,239.976 241.678,241.291 C242.002,317.742 242.323,394.199 242.644,470.658 C242.649,471.968 242.558,473.277 242.52,474.596 C242.263,474.962 242.006,475.35 241.749,475.729 Z" style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape5">
|
||||
<desc shapeID="5" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-108.318,-182.376,216.635,364.752)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,347.312,298.584)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath5" d="M241.779,475.041 C241.823,473.722 241.918,472.411 241.918,471.102 C241.92,394.643 241.92,318.187 241.918,241.734 C241.918,240.419 241.823,239.105 241.775,237.792 C241.92,237.512 241.987,237.213 241.972,236.898 C309.884,198.226 377.793,159.56 445.696,120.889 C450.769,128.704 452.744,137.478 452.76,146.652 C452.849,208.728 452.917,270.806 452.736,332.88 C452.681,352.023 444.171,366.698 427.764,376.214 C373.882,407.507 319.941,438.696 266.003,469.888 C258.582,474.178 250.698,476.87 241.988,476.168 C241.917,475.79 241.84,475.406 241.779,475.041 Z" style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape6">
|
||||
<desc shapeID="6" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-209.173,-120.03,418.345,240.06)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,241.891,119.949)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath6" d="M445.695,120.889 C377.793,159.56 309.883,198.227 241.971,236.898 L241.506,236.768 L241.023,236.777 C226.17,228.203 211.351,219.569 196.457,211.075 C144.401,181.379 92.3218,151.73 40.2466,122.068 C39.549,121.673 38.8075,121.358 38.0875,121.005 C42.2255,113.259 48.4564,107.682 55.8778,103.382 C109.44,72.3605 163.029,41.3904 216.569,10.3335 C233.68,0.406021 250.641,0.650581 267.64,10.503 C320.183,40.949 372.725,71.4076 425.3,101.808 C433.575,106.593 440.976,112.225 445.695,120.889 Z" style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape7">
|
||||
<desc shapeID="7" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(0,-122.427,0,244.854)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,241.774,356.73)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath7" d="M241.774,237.446 L241.774,476.015 " style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill-rule:nonzero;fill:#cc9966;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape8">
|
||||
<desc shapeID="8" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-104.647,-59.991,209.295,119.982)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,343.335,178.995)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath8" d="M241.373,237.446 L445.296,120.543 " style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape9">
|
||||
<desc shapeID="9" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-104.317,-59.7325,208.634,119.465)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,140.134,179.246)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath9" d="M241.774,237.446 L38.4948,121.047 " style="stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape10">
|
||||
<desc shapeID="10" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-116.719,-50.1968,233.439,100.394)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,123.61,131.711)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath10" d="M31.8713,101.652 C75.5019,65.6035 114.27,88.3308 153.403,111.242 C176.076,124.503 197.556,141.171 225.788,141.12 C229.632,141.109 233.478,140.388 237.334,139.99 C233.446,142.396 229.792,145.262 225.668,147.144 C195.444,160.917 166.959,154.378 138.428,140.68 C102.014,123.184 65.7293,118.657 31.7875,147.135 C22.0003,156.17 16.6191,167.316 15.7149,180.62 C9.33591,166.437 8.39471,151.839 11.9082,136.747 C15.1079,123.026 22.6044,111.857 31.8713,101.652 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape11">
|
||||
<desc shapeID="11" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-139.358,-59.8922,278.715,119.784)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,142.781,355.944)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath11" d="M33.2938,391.855 C85.4323,434.83 131.691,407.666 178.395,380.273 C205.453,364.4 231.095,344.483 264.77,344.509 C269.365,344.524 273.956,345.378 278.562,345.848 C273.909,342.986 269.542,339.577 264.627,337.339 C228.526,320.919 194.529,328.747 160.482,345.148 C117.025,366.063 73.7121,371.506 33.1457,337.566 C21.4566,326.78 15.0231,313.487 13.9279,297.589 C6.32814,314.527 5.22034,331.964 9.43433,349.968 C13.2751,366.341 22.239,379.672 33.2938,391.855 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape12">
|
||||
<desc shapeID="12" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-111.465,-47.9396,222.93,95.8792)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.974333,0,0,0.974333,364.67,280.641)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath12" d="M452.278,309.349 C410.61,343.775 373.587,322.074 336.208,300.19 C314.559,287.527 294.044,271.609 267.091,271.658 C263.414,271.668 259.741,272.358 256.066,272.734 C259.768,270.44 263.266,267.703 267.203,265.905 C296.064,252.752 323.269,258.995 350.517,272.075 C385.294,288.784 419.944,293.107 452.359,265.914 C461.707,257.281 466.843,246.637 467.71,233.932 C473.798,247.479 474.7,261.419 471.343,275.831 C468.287,288.933 461.126,299.599 452.278,309.349 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape13">
|
||||
<desc shapeID="13" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-17.7181,-35.368,35.4362,70.736)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,447.867,240.479)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath13" d="M440.534,274.073 C439.242,274.073 437.942,273.81 436.713,273.269 C433.253,271.749 431.026,268.322 431.037,264.545 C431.066,255.469 431.09,246.391 431.114,237.313 L431.173,216.355 C431.184,212.805 433.172,209.558 436.329,207.935 C437.698,207.232 439.188,206.885 440.671,206.885 C442.609,206.885 444.537,207.478 446.177,208.643 C453.492,213.846 459.4,220.987 463.734,229.869 C464.463,231.362 464.787,233.022 464.674,234.679 C463.685,249.226 457.731,261.633 446.977,271.557 C445.187,273.207 442.875,274.073 440.534,274.073 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape14">
|
||||
<desc shapeID="14" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-18.0811,-36.5665,36.1622,73.133)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,32.3325,176.134)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath14" d="M39.8723,210.867 C37.9309,210.867 35.9998,210.273 34.3595,209.104 C26.7569,203.687 20.621,196.266 16.1206,187.046 C15.3921,185.553 15.0673,183.894 15.1803,182.236 C16.2071,167.121 22.3934,154.23 33.5673,143.919 C35.3568,142.268 37.6678,141.402 40.0101,141.402 C41.3018,141.402 42.6022,141.665 43.8313,142.206 C47.2896,143.726 49.518,147.151 49.5066,150.929 C49.5066,150.929 49.3954,192.496 49.3688,201.399 C49.3584,204.949 47.3675,208.198 44.2084,209.82 C42.8425,210.52 41.355,210.867 39.8723,210.867 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape15">
|
||||
<desc shapeID="15" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-19.6619,-41.7165,39.3237,83.433)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,33.7594,302.889)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath15" d="M42.9384,342.513 C40.5952,342.513 38.2842,341.646 36.4947,339.995 C23.5095,328.01 16.3125,313.034 15.1062,295.484 C14.9923,293.832 15.3133,292.18 16.0352,290.689 C21.2213,279.989 28.3394,271.357 37.1891,265.034 C38.8313,263.861 40.7671,263.265 42.7114,263.265 C44.1894,263.265 45.673,263.609 47.0379,264.308 C50.199,265.925 52.1937,269.171 52.2089,272.721 C52.282,289.277 52.34,305.858 52.3979,322.425 L52.4349,332.981 C52.4482,336.76 50.2199,340.186 46.7615,341.708 C45.5324,342.249 44.2312,342.513 42.9384,342.513 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape16">
|
||||
<desc shapeID="16" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-10.5076,-10.568,21.0153,21.136)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,428.894,204.957)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath16" d="M428.626,214.995 C428.004,214.995 427.378,214.934 426.752,214.808 C423.029,214.059 420.11,211.165 419.33,207.448 C419.238,207.017 419.154,206.587 419.073,206.154 C418.561,203.395 419.294,200.551 421.077,198.385 C422.86,196.218 425.51,194.951 428.315,194.923 C428.667,194.919 429.018,194.919 429.376,194.919 C432.948,194.919 436.219,196.923 437.84,200.107 C439.461,203.291 439.159,207.115 437.058,210.003 C436.904,210.214 436.759,210.429 436.613,210.641 L436.441,210.889 C434.649,213.491 431.708,214.995 428.626,214.995 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape17">
|
||||
<desc shapeID="17" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-115.998,-188.608,231.995,377.216)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,138.865,295.82)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath17" d="M236.483,474.968 C228.539,474.968 220.531,472.664 212.681,468.122 C159.199,437.226 106.933,406.97 57.3418,378.199 C38.6404,367.346 28.744,350.055 28.7213,328.196 C28.6785,288.698 28.6842,248.545 28.6899,209.713 C28.6928,190.074 28.6956,170.435 28.6928,150.797 C28.689,139.656 31.4264,129.665 36.831,121.103 C38.6252,118.261 41.7008,116.673 44.8695,116.673 C46.2914,116.673 47.7304,116.992 49.082,117.663 C49.2615,117.752 49.443,117.836 49.6244,117.922 C50.1705,118.179 50.8506,118.499 51.5915,118.917 L85.2339,138.079 C123.772,160.028 162.308,181.976 200.83,203.952 C210.766,209.616 220.821,215.437 230.544,221.068 C234.841,223.556 239.139,226.044 243.438,228.526 C244.551,229.168 245.521,230.03 246.292,231.058 C246.553,231.408 246.817,231.767 247.071,232.116 C248.182,233.636 248.816,235.452 248.892,237.334 L248.941,238.365 C248.987,239.303 249.039,240.366 249.039,241.484 C249.046,314.507 249.046,387.532 249.039,460.561 C249.039,461.617 248.992,462.622 248.95,463.509 C248.932,463.89 248.914,464.273 248.901,464.656 C248.835,466.521 248.222,468.325 247.138,469.842 C247.017,470.012 246.899,470.186 246.782,470.361 L246.494,470.782 C244.85,473.173 242.202,474.68 239.306,474.876 C238.37,474.936 237.419,474.968 236.483,474.968 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape18">
|
||||
<desc shapeID="18" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-116.125,-188.68,232.249,377.361)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,340.204,295.784)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath18" d="M242.242,475 C241.116,475 239.974,474.954 238.849,474.863 C234.597,474.52 231.096,471.386 230.287,467.199 C230.209,466.798 230.132,466.39 230.062,465.999 C229.942,465.334 229.893,464.659 229.918,463.984 C229.934,463.526 229.955,463.069 229.977,462.615 C230.009,461.927 230.045,461.243 230.045,460.558 C230.051,387.53 230.051,314.507 230.045,241.483 C230.045,240.833 230.007,240.088 229.969,239.301 C229.949,238.902 229.929,238.503 229.913,238.104 C229.873,237.13 229.984,236.158 230.237,235.225 C230.721,232.476 232.401,230.033 234.893,228.615 L429.474,117.814 C430.955,116.97 432.57,116.568 434.166,116.568 C437.284,116.568 440.329,118.104 442.141,120.896 C447.615,129.33 450.4,139.342 450.421,150.653 C450.509,216.623 450.562,271.608 450.396,328.569 C450.336,350.194 440.451,367.339 421.811,378.151 C369.704,408.406 317.643,438.508 267.297,467.62 C258.717,472.585 250.52,475 242.242,475 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape19">
|
||||
<desc shapeID="19" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-214.936,-127.599,429.872,255.198)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,239.52,125.167)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath19" d="M239.593,246.366 C239.035,246.366 238.475,246.317 237.92,246.218 C236.522,246.104 235.162,245.682 233.942,244.976 C229.636,242.491 225.333,239.999 221.03,237.508 C211.332,231.893 201.303,226.087 191.42,220.452 C152.902,198.48 114.373,176.536 75.8418,154.591 L42.2242,135.443 C42.0247,135.33 41.7758,135.223 41.5317,135.107 C41.2354,134.969 40.939,134.828 40.6455,134.683 C38.3431,133.541 36.6021,131.517 35.8175,129.069 C35.0339,126.623 35.2751,123.962 36.4862,121.696 C40.9799,113.286 47.7209,106.557 57.092,101.124 C75.9872,90.181 94.8833,79.2445 113.78,68.309 C146.048,49.6352 178.316,30.9613 210.564,12.2552 C220.044,6.75654 229.782,3.96877 239.509,3.96877 C249.325,3.96877 259.206,6.81068 268.875,12.4147 L291.293,25.4057 C333.291,49.7434 376.719,74.9104 419.451,99.6176 C427.019,103.994 436.483,110.438 442.517,121.523 C444.991,126.068 443.373,131.757 438.875,134.318 L244.294,245.122 C242.848,245.945 241.227,246.366 239.593,246.366 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape20">
|
||||
<desc shapeID="20" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-10,-129.947,20,259.894)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,239.404,351.32)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath20" d="M239.404,474.749 C234.158,474.749 229.906,470.496 229.906,465.251 L229.906,237.39 C229.906,232.144 234.158,227.892 239.404,227.892 C244.65,227.892 248.903,232.144 248.903,237.39 L248.903,465.252 C248.903,470.497 244.649,474.749 239.404,474.749 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape21">
|
||||
<desc shapeID="21" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-112.528,-68.7781,225.056,137.556)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,336.407,181.561)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath21" d="M239.033,246.889 C235.74,246.889 232.539,245.175 230.784,242.114 C228.174,237.562 229.749,231.758 234.3,229.149 L429.067,117.493 C433.62,114.883 439.423,116.458 442.032,121.009 C444.64,125.561 443.066,131.364 438.515,133.973 L243.749,245.63 C242.259,246.484 240.635,246.889 239.033,246.889 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape22">
|
||||
<desc shapeID="22" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-112.204,-68.5237,224.408,137.047)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,142.327,181.804)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath22" d="M239.395,246.89 C237.794,246.89 236.172,246.485 234.684,245.633 L40.5315,134.46 C35.9799,131.852 34.4022,126.05 37.0086,121.497 C39.6149,116.946 45.4175,115.368 49.971,117.974 L244.123,229.147 C248.675,231.755 250.252,237.557 247.646,242.11 C245.893,245.174 242.69,246.89 239.395,246.89 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape23">
|
||||
<desc shapeID="23" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-124.348,-59.179,248.696,118.358)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,127.718,136.167)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath23" d="M24.6531,192.378 C20.9686,192.378 17.5473,190.229 15.9943,186.775 C9.2666,171.815 7.84564,155.683 11.7694,138.825 C14.8336,125.69 21.5974,113.695 33.0563,101.073 C33.3612,100.737 33.6898,100.423 34.0394,100.135 C50.4735,86.5573 67.3389,79.9569 85.6005,79.9569 C112.348,79.9569 137.034,94.4106 160.909,108.388 C164.637,110.569 168.297,112.82 171.837,114.997 C189.107,125.62 205.42,135.654 225.171,135.654 L225.282,135.654 C227.213,135.649 229.389,135.367 231.693,135.068 C232.909,134.911 234.127,134.754 235.346,134.628 C235.677,134.594 236.007,134.576 236.334,134.576 C240.356,134.576 243.993,137.13 245.317,141.004 C246.748,145.194 245.096,149.817 241.332,152.149 C240.288,152.796 239.224,153.51 238.097,154.264 C235.48,156.017 232.513,158.004 229.135,159.548 C217.276,164.951 204.871,167.69 192.259,167.691 C175.525,167.692 158.204,163.118 137.751,153.299 C120.308,144.918 105.059,140.844 91.1304,140.844 C74.8112,140.844 60.141,146.467 46.2904,158.031 C38.8028,165.006 34.8258,173.347 34.1344,183.525 C33.839,187.872 30.6228,191.461 26.3343,192.231 C25.771,192.33 25.2087,192.378 24.6531,192.378 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape24">
|
||||
<desc shapeID="24" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-146.526,-68.68,293.052,137.36)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,147.66,350.567)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath24" d="M97.369,415.802 C76.0384,415.8 56.3036,408.079 37.0342,392.197 C36.6809,391.905 36.3484,391.588 36.0407,391.249 C22.5824,376.414 14.6389,362.362 11.0428,347.026 C6.42467,327.307 8.06409,308.439 15.9155,290.943 C17.4666,287.485 20.8907,285.332 24.5771,285.332 C25.1308,285.332 25.6893,285.381 26.2497,285.48 C30.5392,286.246 33.7582,289.832 34.0574,294.179 C34.9275,306.832 39.8847,317.203 49.2121,325.879 C66.1012,339.952 83.9943,346.793 103.909,346.793 C120.844,346.792 139.335,341.854 160.436,331.697 C184.606,320.053 205.051,314.629 224.776,314.629 C239.537,314.629 254.066,317.834 267.958,324.153 C271.831,325.914 275.142,328.122 278.345,330.257 C279.721,331.175 281.021,332.042 282.314,332.836 C286.087,335.158 287.755,339.778 286.333,343.973 C285.016,347.86 281.374,350.426 277.342,350.426 C277.022,350.426 276.699,350.409 276.374,350.376 C274.896,350.226 273.42,350.038 271.945,349.848 C269.16,349.49 266.531,349.153 264.119,349.142 C239.963,349.142 220.241,361.305 199.362,374.181 C195.164,376.77 190.824,379.447 186.473,381.998 C158.098,398.64 128.833,415.801 97.3756,415.801 C97.3728,415.802 97.3728,415.802 97.369,415.802 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape25">
|
||||
<desc shapeID="25" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-119.201,-56.968,238.402,113.936)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,356.778,278.645)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath25" d="M396.996,332.756 C371.338,332.756 347.712,318.924 324.864,305.548 C321.292,303.459 317.795,301.308 314.413,299.227 C297.971,289.113 282.442,279.558 263.707,279.558 L263.602,279.558 C261.804,279.563 259.754,279.83 257.583,280.112 C256.401,280.266 255.217,280.419 254.034,280.541 C253.704,280.575 253.376,280.593 253.051,280.593 C249.029,280.593 245.393,278.039 244.068,274.167 C242.637,269.98 244.285,265.36 248.045,263.025 C249.026,262.415 250.03,261.743 251.093,261.031 C253.619,259.338 256.481,257.42 259.753,255.927 C271.129,250.743 283.032,248.116 295.135,248.116 C311.184,248.116 327.784,252.496 347.378,261.903 C363.98,269.878 378.482,273.756 391.719,273.756 C407.195,273.756 421.112,268.419 434.254,257.447 C441.318,250.864 445.069,242.993 445.722,233.39 C446.017,229.043 449.231,225.454 453.521,224.685 C454.083,224.584 454.647,224.535 455.202,224.535 C458.886,224.535 462.306,226.684 463.86,230.136 C470.326,244.505 471.693,260.004 467.922,276.202 C464.979,288.817 458.488,300.333 447.493,312.449 C447.188,312.785 446.86,313.1 446.509,313.389 C430.734,326.42 414.538,332.756 396.996,332.756 Z" style="stroke:none;fill-rule:nonzero;fill:#99ccff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape26">
|
||||
<desc shapeID="26" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-7.71851,-25.3685,15.437,50.737)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,447.866,240.478)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath26" d="M455.197,234.033 C454.373,246.165 449.466,256.334 440.535,264.574 C440.585,248.513 440.623,232.443 440.671,216.382 C447.099,220.953 451.763,226.996 455.197,234.033 Z" style="stroke:none;fill-rule:evenodd;fill:#0256d1;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape27">
|
||||
<desc shapeID="27" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-8.0815,-26.567,16.163,53.134)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,32.334,176.135)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath27" d="M24.6578,182.88 C25.5212,170.174 30.6608,159.527 40.0101,150.9 C39.9607,167.718 39.9227,184.549 39.8724,201.369 C33.1389,196.572 28.2558,190.251 24.6578,182.88 Z" style="stroke:none;fill-rule:evenodd;fill:#0256d1;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape28">
|
||||
<desc shapeID="28" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-9.662,-31.717,19.324,63.434)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,33.7601,302.887)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath28" d="M24.5828,294.832 C25.6267,310.016 31.7731,322.709 42.9375,333.013 C42.8663,312.938 42.7998,292.831 42.7114,272.761 C34.6929,278.491 28.8495,286.028 24.5828,294.832 Z" style="stroke:none;fill-rule:evenodd;fill:#0256d1;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape29">
|
||||
<desc shapeID="29" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-0.508026,-0.567505,1.01602,1.13499)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,428.894,204.957)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath29" d="M428.625,205.496 C428.55,205.139 428.479,204.781 428.412,204.421 C428.732,204.418 429.051,204.418 429.377,204.418 C429.118,204.774 428.872,205.138 428.625,205.496 Z" style="stroke:none;fill-rule:evenodd;fill:#683dfe;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape30">
|
||||
<desc shapeID="30" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-105.998,-178.607,211.995,357.214)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,138.866,295.822)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath30" d="M238.67,465.396 C231.005,465.912 223.97,463.681 217.438,459.901 C165.637,429.976 113.858,400.006 62.109,369.984 C46.2534,360.782 38.2396,346.73 38.2206,328.187 C38.156,269.054 38.2016,209.924 38.1921,150.797 C38.1892,141.959 40.1279,133.679 44.8647,126.174 C45.5533,126.515 46.26,126.815 46.9268,127.192 C96.6652,155.524 146.406,183.841 196.125,212.202 C210.352,220.313 224.507,228.564 238.69,236.752 C238.929,237.071 239.171,237.4 239.403,237.719 C239.455,238.973 239.542,240.228 239.542,241.483 C239.549,314.508 239.549,387.531 239.542,460.559 C239.542,461.811 239.454,463.059 239.409,464.32 C239.16,464.67 238.918,465.036 238.67,465.396 Z" style="stroke:none;fill-rule:evenodd;fill:url(#linearGradientFill30);fill-opacity:1;"/><defs><linearGradient id="linearGradientFill30" gradientUnits="userSpaceOnUse" x1="138.866" y1="126.173" x2="138.866" y2="465.47"><stop offset="0" stop-color="#00223d" stop-opacity="1"/><stop offset="0.1241" stop-color="#002542" stop-opacity="1"/><stop offset="0.5866" stop-color="#002d4f" stop-opacity="1"/><stop offset="1" stop-color="#003054" stop-opacity="1"/><stop offset="1" stop-color="#005699" stop-opacity="1"/></linearGradient></defs>
|
||||
</g>
|
||||
<g id="Shape31">
|
||||
<desc shapeID="31" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-106.124,-178.681,212.248,357.362)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,340.205,295.783)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath31" d="M239.41,464.319 C239.455,463.058 239.543,461.81 239.543,460.558 C239.55,387.53 239.55,314.507 239.543,241.482 C239.543,240.227 239.455,238.972 239.404,237.718 C239.55,237.45 239.613,237.164 239.594,236.868 C304.456,199.93 369.319,162.999 434.175,126.065 C439.019,133.529 440.908,141.908 440.923,150.669 C441.002,209.963 441.071,269.251 440.898,328.539 C440.847,346.823 432.72,360.841 417.047,369.932 C365.583,399.813 314.062,429.605 262.544,459.395 C255.457,463.497 247.929,466.066 239.612,465.395 C239.543,465.036 239.474,464.67 239.41,464.319 Z" style="stroke:none;fill-rule:evenodd;fill:url(#linearGradientFill31);fill-opacity:1;"/><defs><linearGradient id="linearGradientFill31" gradientUnits="userSpaceOnUse" x1="287.104" y1="209.438" x2="389.198" y2="386.271"><stop offset="0" stop-color="#003366" stop-opacity="1"/><stop offset="0.4667" stop-color="#003468" stop-opacity="1"/><stop offset="0.6674" stop-color="#00396f" stop-opacity="1"/><stop offset="0.8171" stop-color="#00427c" stop-opacity="1"/><stop offset="0.9406" stop-color="#004e8d" stop-opacity="1"/><stop offset="1" stop-color="#005699" stop-opacity="1"/></linearGradient></defs>
|
||||
</g>
|
||||
<g id="Shape32">
|
||||
<desc shapeID="32" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-204.934,-117.598,409.868,235.197)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,239.52,125.169)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath32" d="M434.175,126.066 C369.319,163 304.456,199.931 239.594,236.869 L239.151,236.742 L238.691,236.752 C224.508,228.564 210.352,220.313 196.126,212.202 C146.407,183.841 96.6661,155.523 46.9277,127.191 C46.261,126.815 45.5543,126.515 44.8657,126.174 C48.8189,118.777 54.7706,113.452 61.8573,109.344 C113.013,79.7156 164.198,50.1348 215.332,20.4732 C231.679,10.9919 247.881,11.2255 264.113,20.6346 C314.298,49.7149 364.485,78.8095 414.697,107.842 C422.6,112.411 429.669,117.789 434.175,126.066 Z" style="stroke:none;fill-rule:evenodd;fill:url(#linearGradientFill32);fill-opacity:1;"/><defs><linearGradient id="linearGradientFill32" gradientUnits="userSpaceOnUse" x1="239.519" y1="13.4682" x2="239.519" y2="236.868"><stop offset="0" stop-color="#003366" stop-opacity="1"/><stop offset="0.3992" stop-color="#004580" stop-opacity="1"/><stop offset="0.644" stop-color="#004d8c" stop-opacity="1"/><stop offset="1" stop-color="#005699" stop-opacity="1"/></linearGradient></defs>
|
||||
</g>
|
||||
<g id="Shape33">
|
||||
<desc shapeID="33" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(0,-119.947,0,239.894)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,239.404,351.32)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath33" d="M239.404,237.39 L239.404,465.251 " style="stroke:#336699;stroke-opacity:1;stroke-width:0.5;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape34">
|
||||
<desc shapeID="34" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-102.526,-58.7765,205.053,117.553)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,336.408,181.562)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath34" d="M239.024,237.39 L433.792,125.733 " style="stroke:#336699;stroke-opacity:1;stroke-width:0.5;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape35">
|
||||
<desc shapeID="35" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-102.203,-58.522,204.406,117.044)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,142.328,181.803)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath35" d="M239.404,237.39 L45.2513,126.217 " style="stroke:#336699;stroke-opacity:1;stroke-width:0.5;stroke-linejoin:miter;stroke-miterlimit:2;stroke-linecap:butt;fill:none;"/>
|
||||
</g>
|
||||
<g id="Shape36">
|
||||
<desc shapeID="36" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-114.355,-49.1795,228.711,98.359)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,127.71,136.167)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath36" d="M40.0899,107.457 C81.7612,73.0278 118.79,94.7336 156.167,116.618 C177.822,129.283 198.337,145.203 225.302,145.152 C228.978,145.143 232.649,144.458 236.33,144.075 C232.618,146.375 229.129,149.109 225.191,150.907 C196.32,164.062 169.113,157.818 141.863,144.735 C107.084,128.025 72.4281,123.702 40.0101,150.9 C30.6608,159.527 25.5212,170.175 24.6578,182.88 C18.5655,169.333 17.667,155.391 21.0209,140.978 C24.0784,127.873 31.2402,117.205 40.0899,107.457 Z" style="stroke:none;fill-rule:evenodd;fill:#0091fc;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape37">
|
||||
<desc shapeID="37" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-136.535,-58.6793,273.07,117.359)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,147.652,350.567)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath37" d="M43.0762,384.867 C92.8744,425.912 137.06,399.967 181.668,373.806 C207.508,358.645 231.997,339.621 264.163,339.643 C268.553,339.663 272.936,340.478 277.338,340.926 C272.895,338.193 268.724,334.935 264.027,332.799 C229.549,317.115 197.077,324.588 164.557,340.256 C123.052,360.234 81.6843,365.432 42.9375,333.013 C31.7731,322.709 25.6267,310.016 24.5828,294.832 C17.3241,311.008 16.2641,327.66 20.2914,344.858 C23.9578,360.496 32.5168,373.228 43.0762,384.867 Z" style="stroke:none;fill-rule:evenodd;fill:#0091fc;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape38">
|
||||
<desc shapeID="38" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-109.208,-46.9687,218.415,93.9375)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(0.94984,0,0,0.94984,356.784,278.645)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath38" d="M440.459,306.065 C400.662,338.946 365.302,318.216 329.6,297.315 C308.926,285.222 289.325,270.012 263.583,270.061 C260.077,270.07 256.568,270.73 253.054,271.093 C256.591,268.897 259.933,266.284 263.694,264.568 C291.259,252.006 317.244,257.972 343.266,270.464 C376.48,286.422 409.578,290.553 440.534,264.573 C449.464,256.333 454.371,246.164 455.197,234.032 C461.018,246.969 461.874,260.282 458.67,274.047 C455.75,286.565 448.912,296.75 440.459,306.065 Z" style="stroke:none;fill-rule:evenodd;fill:#0091fc;fill-opacity:1;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 170 KiB |
|
|
@ -35,6 +35,10 @@ const Image = (props: Props) => {
|
|||
return '/static/media/placeholder_pkg_opa.png';
|
||||
case RepositoryKind.Falco:
|
||||
return '/static/media/placeholder_pkg_falco.png';
|
||||
case RepositoryKind.TBAction:
|
||||
return '/static/media/placeholder_pkg_tbaction.png';
|
||||
case RepositoryKind.Krew:
|
||||
return '/static/media/placeholder_pkg_krew.png';
|
||||
default:
|
||||
return PLACEHOLDER_SRC;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,20 @@ describe('RepositoryIcon', () => {
|
|||
expect(icon).toHaveProperty('src', 'http://localhost/static/media/olm-operators.svg');
|
||||
});
|
||||
|
||||
it('renders Tinkerbell icon', () => {
|
||||
const { getByAltText } = render(<RepositoryIcon kind={RepositoryKind.TBAction} />);
|
||||
const icon = getByAltText('Icon');
|
||||
expect(icon).toBeInTheDocument();
|
||||
expect(icon).toHaveProperty('src', 'http://localhost/static/media/tinkerbell-actions.svg');
|
||||
});
|
||||
|
||||
it('renders Krew icon', () => {
|
||||
const { getByAltText } = render(<RepositoryIcon kind={RepositoryKind.Krew} />);
|
||||
const icon = getByAltText('Icon');
|
||||
expect(icon).toBeInTheDocument();
|
||||
expect(icon).toHaveProperty('src', 'http://localhost/static/media/krew-plugins.svg');
|
||||
});
|
||||
|
||||
it('renders Chart icon - light version', () => {
|
||||
const { getByAltText } = render(<RepositoryIcon kind={RepositoryKind.Helm} type="white" />);
|
||||
const icon = getByAltText('Icon');
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ const ICONS = {
|
|||
default: '/static/media/tinkerbell-actions.svg',
|
||||
white: '/static/media/tinkerbell-actions-light.svg',
|
||||
},
|
||||
[RepositoryKind.Krew]: {
|
||||
default: '/static/media/krew-plugins.svg',
|
||||
white: '/static/media/krew-plugins-light.svg',
|
||||
},
|
||||
};
|
||||
|
||||
const RepositoryIcon = (props: Props) => {
|
||||
|
|
|
|||
|
|
@ -211,6 +211,13 @@ const RepositoryModal = (props: Props) => {
|
|||
</ExternalLink>
|
||||
);
|
||||
break;
|
||||
case RepositoryKind.Krew:
|
||||
link = (
|
||||
<ExternalLink href="/docs/repositories#krew-kubectl-plugins-repositories" className="text-reset">
|
||||
<u>Krew kubectl plugins</u>
|
||||
</ExternalLink>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (isUndefined(link)) return;
|
||||
|
|
@ -449,9 +456,13 @@ const RepositoryModal = (props: Props) => {
|
|||
|
||||
{getAdditionalInfo()}
|
||||
|
||||
{[RepositoryKind.Falco, RepositoryKind.OLM, RepositoryKind.OPA, RepositoryKind.TBAction].includes(
|
||||
selectedKind
|
||||
) && (
|
||||
{[
|
||||
RepositoryKind.Falco,
|
||||
RepositoryKind.OLM,
|
||||
RepositoryKind.OPA,
|
||||
RepositoryKind.TBAction,
|
||||
RepositoryKind.Krew,
|
||||
].includes(selectedKind) && (
|
||||
<div className="mt-4">
|
||||
<InputField
|
||||
type="text"
|
||||
|
|
@ -502,28 +513,30 @@ const RepositoryModal = (props: Props) => {
|
|||
</small>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 mb-3">
|
||||
<div className="custom-control custom-switch pl-0">
|
||||
<input
|
||||
id="scannerDisabledRepo"
|
||||
type="checkbox"
|
||||
className="custom-control-input"
|
||||
value="true"
|
||||
onChange={() => setIsScannerDisabled(!isScannerDisabled)}
|
||||
checked={isScannerDisabled}
|
||||
/>
|
||||
<label
|
||||
htmlFor="scannerDisabledRepo"
|
||||
className={`custom-control-label font-weight-bold ${styles.label} ${styles.customControlRightLabel}`}
|
||||
>
|
||||
Security scanner disabled
|
||||
</label>
|
||||
</div>
|
||||
{selectedKind !== RepositoryKind.Krew && (
|
||||
<div className="mt-4 mb-3">
|
||||
<div className="custom-control custom-switch pl-0">
|
||||
<input
|
||||
id="scannerDisabledRepo"
|
||||
type="checkbox"
|
||||
className="custom-control-input"
|
||||
value="true"
|
||||
onChange={() => setIsScannerDisabled(!isScannerDisabled)}
|
||||
checked={isScannerDisabled}
|
||||
/>
|
||||
<label
|
||||
htmlFor="scannerDisabledRepo"
|
||||
className={`custom-control-label font-weight-bold ${styles.label} ${styles.customControlRightLabel}`}
|
||||
>
|
||||
Security scanner disabled
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<small className="form-text text-muted mt-2">
|
||||
Use this switch to disable the security scanning of the packages in this repository.
|
||||
</small>
|
||||
</div>
|
||||
<small className="form-text text-muted mt-2">
|
||||
Use this switch to disable the security scanning of the packages in this repository.
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
.aboutIcon {
|
||||
width: 75px;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.socialBtn {
|
||||
|
|
|
|||
|
|
@ -324,9 +324,9 @@ exports[`Home index creates snapshot 1`] = `
|
|||
<div
|
||||
class="text-center px-4 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, Falco configurations, Open Policy Agent (OPA) policies, OLM operators and Tinkerbell actions.
|
||||
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, Falco configurations, Open Policy Agent (OPA) policies, OLM operators, Tinkerbell actions and kubectl plugins.
|
||||
<div
|
||||
class="mx-3 mx-lg-5 my-4 my-lg-5 d-flex flex-row align-items-center justify-content-around"
|
||||
class="mx-0 mx-md-3 mx-lg-5 my-4 my-lg-5 d-flex flex-row flex-wrap align-items-center justify-content-around"
|
||||
>
|
||||
<a
|
||||
class="link undefined"
|
||||
|
|
@ -367,6 +367,10 @@ exports[`Home index creates snapshot 1`] = `
|
|||
src="/static/media/opa-policies-light.svg"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="mx-0 mx-md-3 mx-lg-5 my-4 my-lg-5 d-flex flex-row flex-wrap align-items-center justify-content-around"
|
||||
>
|
||||
<a
|
||||
class="link undefined"
|
||||
href="https://github.com/operator-framework"
|
||||
|
|
@ -393,6 +397,19 @@ exports[`Home index creates snapshot 1`] = `
|
|||
src="/static/media/tinkerbell-actions-light.svg"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
class="link undefined"
|
||||
href="https://krew.sigs.k8s.io"
|
||||
rel="noopener noreferrer"
|
||||
role="button"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
alt="Icon"
|
||||
class="aboutIcon repoIcon"
|
||||
src="/static/media/krew-plugins-light.svg"
|
||||
/>
|
||||
</a>
|
||||
</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.
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ describe('Home index', () => {
|
|||
await waitFor(() => expect(API.getStats).toHaveBeenCalledTimes(1));
|
||||
|
||||
const links = getAllByRole('button');
|
||||
expect(links).toHaveLength(11);
|
||||
expect(links).toHaveLength(12);
|
||||
|
||||
expect(links[2]).toHaveProperty('href', 'https://github.com/cncf/hub');
|
||||
expect(links[3]).toHaveProperty('href', 'https://cloud-native.slack.com/channels/artifact-hub');
|
||||
|
|
@ -136,8 +136,9 @@ describe('Home index', () => {
|
|||
expect(links[7]).toHaveProperty('href', 'https://www.openpolicyagent.org/');
|
||||
expect(links[8]).toHaveProperty('href', 'https://github.com/operator-framework');
|
||||
expect(links[9]).toHaveProperty('href', 'https://tinkerbell.org/');
|
||||
expect(links[10]).toHaveProperty('href', 'https://krew.sigs.k8s.io/');
|
||||
|
||||
expect(links[10]).toHaveProperty('href', 'https://www.cncf.io/sandbox-projects/');
|
||||
expect(links[11]).toHaveProperty('href', 'https://www.cncf.io/sandbox-projects/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -149,8 +149,8 @@ const HomeView = (props: Props) => {
|
|||
<div className="text-center px-4 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, Falco configurations, Open
|
||||
Policy Agent (OPA) policies, OLM operators and Tinkerbell actions.
|
||||
<div className="mx-3 mx-lg-5 my-4 my-lg-5 d-flex flex-row align-items-center justify-content-around">
|
||||
Policy Agent (OPA) policies, OLM operators, Tinkerbell actions and kubectl plugins.
|
||||
<div className="mx-0 mx-md-3 mx-lg-5 my-4 my-lg-5 d-flex flex-row flex-wrap align-items-center justify-content-around">
|
||||
<ExternalLink href="https://helm.sh">
|
||||
<RepositoryIcon kind={RepositoryKind.Helm} type="white" className={styles.aboutIcon} />
|
||||
</ExternalLink>
|
||||
|
|
@ -160,12 +160,17 @@ const HomeView = (props: Props) => {
|
|||
<ExternalLink href="https://www.openpolicyagent.org">
|
||||
<RepositoryIcon kind={RepositoryKind.OPA} type="white" className={styles.aboutIcon} />
|
||||
</ExternalLink>
|
||||
</div>
|
||||
<div className="mx-0 mx-md-3 mx-lg-5 my-4 my-lg-5 d-flex flex-row flex-wrap align-items-center justify-content-around">
|
||||
<ExternalLink href="https://github.com/operator-framework">
|
||||
<RepositoryIcon kind={RepositoryKind.OLM} type="white" className={styles.aboutIcon} />
|
||||
</ExternalLink>
|
||||
<ExternalLink href="https://tinkerbell.org">
|
||||
<RepositoryIcon kind={RepositoryKind.TBAction} type="white" className={styles.aboutIcon} />
|
||||
</ExternalLink>
|
||||
<ExternalLink href="https://krew.sigs.k8s.io">
|
||||
<RepositoryIcon kind={RepositoryKind.Krew} type="white" className={styles.aboutIcon} />
|
||||
</ExternalLink>
|
||||
</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
|
||||
|
|
|
|||
|
|
@ -81,6 +81,22 @@ describe('ChangelogModal', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('does not render component when repo kind is Krew or Falco', async () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
packageItem: {
|
||||
...defaultProps.packageItem,
|
||||
repository: {
|
||||
...defaultProps.packageItem.repository,
|
||||
kind: 5,
|
||||
},
|
||||
},
|
||||
};
|
||||
const { container } = render(<ChangelogModal {...props} />);
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('renders disabled button when package has not changelog and does not call getChangelog', async () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ const ChangelogModal = (props: Props) => {
|
|||
}
|
||||
}, []); /* eslint-disable-line react-hooks/exhaustive-deps */
|
||||
|
||||
if (props.packageItem.repository.kind === RepositoryKind.Falco) return null;
|
||||
if ([RepositoryKind.Falco, RepositoryKind.Krew].includes(props.packageItem.repository.kind)) return null;
|
||||
|
||||
const sortChangelog = (items: ChangeLog[]): ChangeLog[] => {
|
||||
const validVersions: ChangeLog[] = items.filter((item: ChangeLog) => semver.valid(item.version));
|
||||
|
|
|
|||
|
|
@ -232,4 +232,8 @@
|
|||
.relatedPackagesWrapper {
|
||||
max-width: 310px;
|
||||
}
|
||||
|
||||
.noReadmeWrapper {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,10 @@ const Readme = (props: Props) => {
|
|||
}
|
||||
};
|
||||
|
||||
const LinkRef: React.ElementType = (data: LinkProps) => {
|
||||
return <span className="font-weight-bold">{data.children}</span>;
|
||||
};
|
||||
|
||||
const Table: React.ElementType = (data: TableProps) => (
|
||||
<div className="mw-100 overflow-auto">
|
||||
<table>{data.children}</table>
|
||||
|
|
@ -117,6 +121,7 @@ const Readme = (props: Props) => {
|
|||
code: Code,
|
||||
image: Image,
|
||||
link: Link,
|
||||
linkReference: LinkRef,
|
||||
table: Table,
|
||||
heading: Heading,
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { Repository } from '../../../types';
|
||||
import KrewInstall from './KrewInstall';
|
||||
|
||||
const repo: Repository = {
|
||||
kind: 5,
|
||||
name: 'repo',
|
||||
displayName: 'Repo',
|
||||
url: 'http://repo.test',
|
||||
userAlias: 'user',
|
||||
};
|
||||
const defaultProps = {
|
||||
name: 'packageName',
|
||||
repository: repo,
|
||||
};
|
||||
|
||||
describe('KrewInstall', () => {
|
||||
it('creates snapshot', () => {
|
||||
const result = render(<KrewInstall {...defaultProps} />);
|
||||
expect(result.asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('Render', () => {
|
||||
it('renders component', () => {
|
||||
const { getByText } = render(<KrewInstall {...defaultProps} />);
|
||||
|
||||
expect(getByText('Add repository')).toBeInTheDocument();
|
||||
expect(getByText('kubectl krew index add repo http://repo.test')).toBeInTheDocument();
|
||||
|
||||
expect(getByText('Install plugin')).toBeInTheDocument();
|
||||
expect(getByText('kubectl krew install repo/packageName')).toBeInTheDocument();
|
||||
|
||||
const link = getByText('Need Krew?');
|
||||
expect(link).toBeInTheDocument();
|
||||
expect(link).toHaveProperty('href', 'https://krew.sigs.k8s.io/docs/user-guide/setup/install/');
|
||||
});
|
||||
|
||||
it('renders component when is default Krew repo', () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
repository: { ...defaultProps.repository, url: 'https://github.com/kubernetes-sigs/krew-index' },
|
||||
};
|
||||
const { getByText, queryByText } = render(<KrewInstall {...props} />);
|
||||
|
||||
expect(queryByText('Add repository')).toBeNull();
|
||||
expect(queryByText('kubectl krew index add repo http://repo.test')).toBeNull();
|
||||
|
||||
expect(getByText('Install plugin')).toBeInTheDocument();
|
||||
expect(getByText('kubectl krew install packageName')).toBeInTheDocument();
|
||||
|
||||
const link = getByText('Need Krew?');
|
||||
expect(link).toBeInTheDocument();
|
||||
expect(link).toHaveProperty('href', 'https://krew.sigs.k8s.io/docs/user-guide/setup/install/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Repository } from '../../../types';
|
||||
import ExternalLink from '../../common/ExternalLink';
|
||||
import CommandBlock from './CommandBlock';
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
repository: Repository;
|
||||
}
|
||||
|
||||
const DEFAULT_REPO = 'https://github.com/kubernetes-sigs/krew-index';
|
||||
|
||||
const KrewInstall = (props: Props) => {
|
||||
const isDefaultRepo = props.repository.url.startsWith(DEFAULT_REPO);
|
||||
let installCommand = `kubectl krew install ${props.repository.name}/${props.name}`;
|
||||
if (isDefaultRepo) {
|
||||
installCommand = `kubectl krew install ${props.name}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isDefaultRepo && (
|
||||
<CommandBlock
|
||||
command={`kubectl krew index add ${props.repository.name} ${props.repository.url}`}
|
||||
title="Add repository"
|
||||
/>
|
||||
)}
|
||||
|
||||
<CommandBlock command={installCommand} title="Install plugin" />
|
||||
|
||||
<div className="mt-2 d-flex flex-row justify-content-between align-items-baseline">
|
||||
<ExternalLink href="https://krew.sigs.k8s.io/docs/user-guide/setup/install/" className="btn btn-link pl-0">
|
||||
Need Krew?
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default KrewInstall;
|
||||
|
|
@ -18,6 +18,7 @@ import CustomInstall from './CustomInstall';
|
|||
import FalcoInstall from './FalcoInstall';
|
||||
import HelmInstall from './HelmInstall';
|
||||
import HelmOCIInstall from './HelmOCIInstall';
|
||||
import KrewInstall from './KrewInstall';
|
||||
import OLMInstall from './OLMInstall';
|
||||
import OLMOCIInstall from './OLMOCIInstall';
|
||||
|
||||
|
|
@ -155,6 +156,8 @@ const InstallationModal = (props: Props) => {
|
|||
);
|
||||
case InstallMethodKind.Falco:
|
||||
return <FalcoInstall normalizedName={method.props.normalizedName!} />;
|
||||
case InstallMethodKind.Krew:
|
||||
return <KrewInstall name={method.props.name!} repository={method.props.repository!} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
153
web/src/layout/package/installation/__snapshots__/KrewInstall.test.tsx.snap
generated
Normal file
153
web/src/layout/package/installation/__snapshots__/KrewInstall.test.tsx.snap
generated
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`KrewInstall creates snapshot 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="my-2"
|
||||
>
|
||||
<small
|
||||
class="text-muted mt-2 mb-1"
|
||||
>
|
||||
Add repository
|
||||
</small>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex align-items-start"
|
||||
>
|
||||
<div
|
||||
class="flex-grow-1 mr-3 blockWrapper"
|
||||
>
|
||||
<pre
|
||||
style="display: block; overflow-x: auto; padding: 0.5em; color: rgb(0, 0, 0); background: rgb(248, 248, 255);"
|
||||
>
|
||||
<code
|
||||
class="language-bash"
|
||||
>
|
||||
<span>
|
||||
kubectl krew index add repo http://repo.test
|
||||
</span>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="position-relative undefined"
|
||||
>
|
||||
<button
|
||||
class="btn btn-sm btn-primary rounded-circle copyBtn"
|
||||
data-testid="ctcBtn"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="d-flex flex-row align-items-center"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
height="1em"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
height="13"
|
||||
rx="2"
|
||||
ry="2"
|
||||
width="13"
|
||||
x="9"
|
||||
y="9"
|
||||
/>
|
||||
<path
|
||||
d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="my-2"
|
||||
>
|
||||
<small
|
||||
class="text-muted mt-2 mb-1"
|
||||
>
|
||||
Install plugin
|
||||
</small>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex align-items-start"
|
||||
>
|
||||
<div
|
||||
class="flex-grow-1 mr-3 blockWrapper"
|
||||
>
|
||||
<pre
|
||||
style="display: block; overflow-x: auto; padding: 0.5em; color: rgb(0, 0, 0); background: rgb(248, 248, 255);"
|
||||
>
|
||||
<code
|
||||
class="language-bash"
|
||||
>
|
||||
<span>
|
||||
kubectl krew install repo/packageName
|
||||
</span>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="position-relative undefined"
|
||||
>
|
||||
<button
|
||||
class="btn btn-sm btn-primary rounded-circle copyBtn"
|
||||
data-testid="ctcBtn"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="d-flex flex-row align-items-center"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
height="1em"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
height="13"
|
||||
rx="2"
|
||||
ry="2"
|
||||
width="13"
|
||||
x="9"
|
||||
y="9"
|
||||
/>
|
||||
<path
|
||||
d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mt-2 d-flex flex-row justify-content-between align-items-baseline"
|
||||
>
|
||||
<a
|
||||
class="link btn btn-link pl-0"
|
||||
href="https://krew.sigs.k8s.io/docs/user-guide/setup/install/"
|
||||
rel="noopener noreferrer"
|
||||
role="button"
|
||||
target="_blank"
|
||||
>
|
||||
Need Krew?
|
||||
</a>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
|
@ -6,6 +6,7 @@ export enum RepositoryKind {
|
|||
OPA,
|
||||
OLM,
|
||||
TBAction,
|
||||
Krew,
|
||||
}
|
||||
|
||||
export interface Repository {
|
||||
|
|
|
|||
|
|
@ -101,6 +101,13 @@ export const REPOSITORY_KINDS: RepoKindDef[] = [
|
|||
icon: <RepositoryIcon kind={RepositoryKind.TBAction} className="mw-100 mh-100" />,
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
kind: RepositoryKind.Krew,
|
||||
label: 'krew',
|
||||
name: 'Krew kubectl plugins',
|
||||
icon: <RepositoryIcon kind={RepositoryKind.Krew} className="mw-100 mh-100" />,
|
||||
active: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const PAYLOAD_KINDS_LIST: PayloadKindsItem[] = [
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export enum InstallMethodKind {
|
|||
OLM,
|
||||
OLMOCI,
|
||||
Falco,
|
||||
Krew,
|
||||
}
|
||||
|
||||
const SPECIAL_OLM = 'community-operators';
|
||||
|
|
@ -148,6 +149,19 @@ export default (props: PackageInfo): InstallMethodOutput => {
|
|||
});
|
||||
}
|
||||
break;
|
||||
case RepositoryKind.Krew:
|
||||
if (isUndefined(pkg.install)) {
|
||||
output.methods.push({
|
||||
label: 'krew',
|
||||
title: 'Krew',
|
||||
kind: InstallMethodKind.Krew,
|
||||
props: {
|
||||
name: pkg.name,
|
||||
repository: pkg.repository,
|
||||
},
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue