first commit

This commit is contained in:
Shubham Gupta 2023-04-13 20:00:38 +05:30
commit 7237652c1f
No known key found for this signature in database
7431 changed files with 358645 additions and 0 deletions

30
app.yaml Normal file
View File

@ -0,0 +1,30 @@
# Copyright 2018 Google LLC All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
runtime: go116
service: ${SERVICE}
handlers:
- url: /site/$
static_files: public/index.html
upload: public/index.html
- url: /site/(.*)/$
static_files: public/\1/index.html
upload: public/.*/index.html
- url: /site
static_dir: public
secure: always
- url: /.*
secure: always
script: auto

10
assets/icons/logo.svg Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="64px" height="64px" viewBox="0 0 64 64" version="1.1">
<g id="surface1">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(22.352941%,43.921569%,89.411765%);fill-opacity:1;" d="M 28.3125 0.964844 C 29.824219 0.214844 31.59375 0.214844 33.105469 0.964844 L 52.265625 10.425781 C 53.734375 11.152344 54.796875 12.503906 55.152344 14.105469 L 59.933594 35.59375 C 60.285156 37.167969 59.914062 38.816406 58.921875 40.09375 L 45.585938 57.246094 C 44.5625 58.5625 42.984375 59.335938 41.3125 59.335938 L 20.105469 59.335938 C 18.433594 59.335938 16.855469 58.5625 15.832031 57.246094 L 2.496094 40.09375 C 1.503906 38.816406 1.132812 37.167969 1.484375 35.59375 L 6.265625 14.105469 C 6.621094 12.503906 7.683594 11.152344 9.152344 10.425781 Z M 28.3125 0.964844 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 20.03125 17.519531 C 20.03125 17.519531 17.417969 17.808594 15.675781 18 C 15.519531 18.019531 15.378906 18.125 15.328125 18.285156 C 15.277344 18.445312 15.328125 18.609375 15.445312 18.714844 C 16.738281 19.898438 18.683594 21.667969 18.683594 21.667969 C 18.683594 21.667969 18.152344 24.242188 17.796875 25.957031 C 17.765625 26.113281 17.824219 26.277344 17.957031 26.378906 C 18.09375 26.476562 18.265625 26.476562 18.402344 26.402344 C 19.929688 25.535156 22.210938 24.230469 22.210938 24.230469 C 22.210938 24.230469 24.496094 25.535156 26.019531 26.402344 C 26.160156 26.476562 26.332031 26.476562 26.46875 26.378906 C 26.601562 26.277344 26.660156 26.113281 26.628906 25.960938 C 26.273438 24.242188 25.742188 21.667969 25.742188 21.667969 C 25.742188 21.667969 27.6875 19.898438 28.984375 18.71875 C 29.097656 18.609375 29.152344 18.445312 29.097656 18.285156 C 29.046875 18.125 28.910156 18.019531 28.753906 18.003906 C 27.007812 17.808594 24.394531 17.519531 24.394531 17.519531 C 24.394531 17.519531 23.3125 15.121094 22.589844 13.523438 C 22.523438 13.382812 22.378906 13.28125 22.210938 13.28125 C 22.046875 13.28125 21.902344 13.382812 21.839844 13.523438 C 21.117188 15.121094 20.03125 17.519531 20.03125 17.519531 Z M 20.03125 17.519531 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 35.113281 14.746094 L 46.824219 14.746094 L 46.824219 26.453125 L 35.113281 26.453125 Z M 35.113281 14.746094 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 29.121094 38.957031 C 29.121094 42.722656 26.066406 45.773438 22.304688 45.773438 C 18.539062 45.773438 15.488281 42.722656 15.488281 38.957031 C 15.488281 35.195312 18.539062 32.144531 22.304688 32.144531 C 26.066406 32.144531 29.121094 35.195312 29.121094 38.957031 Z M 29.121094 38.957031 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 46.363281 44.078125 L 34.386719 44.078125 C 34.226562 44.078125 34.074219 43.992188 33.992188 43.851562 C 33.910156 43.714844 33.90625 43.542969 33.984375 43.398438 L 39.972656 32.34375 C 40.050781 32.195312 40.207031 32.101562 40.375 32.101562 C 40.542969 32.101562 40.699219 32.195312 40.78125 32.34375 L 46.769531 43.398438 C 46.847656 43.542969 46.84375 43.714844 46.757812 43.851562 C 46.675781 43.992188 46.527344 44.078125 46.363281 44.078125 Z M 46.363281 44.078125 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,66 @@
/*
Add styles or override variables from the theme here.
*/
$display1-weight: 500 !default;
$display2-weight: 100 !default;
$primary: rgb(255, 255,255) !default;
$primary-light: lighten($primary, 75%) !default;
$secondary: #2D70DE !default;
$light: rgb(255, 255,255) !default;
$grey: #888 !default;
$orange: $secondary;
$td-sidebar-tree-root-color: #222 !default;
.nav-shadow {
box-shadow: 0 2px 2px -2px rgba(0,0,0,.2);
}
.navbar-brand {
color: black !important
}
.navbar-bg-onscroll {
box-shadow: 0 2px 2px -2px rgba(0,0,0,.2);
#agones-top {
display: block !important;
}
}
.control-bar {
display: none;
}
.asciinema-theme-asciinema .asciinema-terminal {
color: #cccccc;
background-color: #121314;
border-color: #121314;
border-radius: 8px;
}
.asciinema-theme-asciinema .bg-fg {
background-color:black !important;
}
#agones-search {
background: rgba(190, 185, 185, 0.3);
}
input[type="search"]::placeholder {
color: #9B9595 !important;
}
.td-box--secondary p > a:hover {
color: #121314 !important;
}
#community a {
color: #121314 !important;
}
.showcase img {
margin: 0 30px;
}

View File

@ -0,0 +1,28 @@
{
"hideMemberFields": [
"TypeMeta"
],
"hideTypePatterns": [
"ParseError$",
"List$"
],
"externalPackages": [
{
"typeMatchPrefix": "^k8s\\.io/apimachinery/pkg/apis/meta/v1\\.Duration$",
"docsURLTemplate": "https://godoc.org/k8s.io/apimachinery/pkg/apis/meta/v1#Duration"
},
{
"typeMatchPrefix": "^k8s\\.io/(api|apimachinery/pkg/apis)/",
"docsURLTemplate": "https://v1-23.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#{{lower .TypeIdentifier}}-{{arrIndex .PackageSegments -1}}-{{arrIndex .PackageSegments -2}}"
},
{
"typeMatchPrefix": "^github\\.com/knative/pkg/apis/duck/",
"docsURLTemplate": "https://godoc.org/github.com/knative/pkg/apis/duck/{{arrIndex .PackageSegments -1}}#{{.TypeIdentifier}}"
}
],
"typeDisplayNamePrefixOverrides": {
"k8s.io/api/": "Kubernetes ",
"k8s.io/apimachinery/pkg/apis/": "Kubernetes "
},
"markdownDisabled": false
}

48
assets/templates/pkg.tpl Normal file
View File

@ -0,0 +1,48 @@
{{ define "packages" }}
{{ with .packages}}
<p>Packages:</p>
<ul>
{{ range . }}
<li>
<a href="#{{- packageAnchorID . -}}">{{ packageDisplayName . }}</a>
</li>
{{ end }}
</ul>
{{ end}}
{{ range .packages }}
<h2 id="{{- packageAnchorID . -}}">
{{- packageDisplayName . -}}
</h2>
{{ with (index .GoPackages 0 )}}
{{ with .DocComments }}
<p>
{{ safe (renderComments .) }}
</p>
{{ end }}
{{ end }}
Resource Types:
<ul>
{{- range (visibleTypes (sortedTypes .Types)) -}}
{{ if isExportedType . -}}
<li>
<a href="{{ linkForType . }}">{{ typeDisplayName . }}</a>
</li>
{{- end }}
{{- end -}}
</ul>
{{ range (visibleTypes (sortedTypes .Types))}}
{{ template "type" . }}
{{ end }}
<hr/>
{{ end }}
<p><em>
Generated with <code>gen-crd-api-reference-docs</code>.
</em></p>
{{ end }}

63
cloudbuild.yaml Normal file
View File

@ -0,0 +1,63 @@
# Copyright 2019 Google LLC All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Google Cloud Builder -- Push site to production
# on merge with main
#
steps:
#
# Creates the initial make + docker build platform
#
- name: "ubuntu"
args: ["bash", "-c", "echo 'FROM gcr.io/cloud-builders/docker\nRUN apt-get install make\nENTRYPOINT [\"/usr/bin/make\"]' > Dockerfile.build"]
- name: "gcr.io/cloud-builders/docker"
args: ['build', '-f', 'Dockerfile.build', '-t', 'make-docker', '.'] # we need docker and make to run everything.
- name: "make-docker"
dir: "build"
args: ["pull-build-image"] # since we are past CI build, we can assume that the build image exists.
#
# Build production site and deploy
#
- name: "make-docker" # build production version of the site
dir: "build"
args: ["site-static", "site-gen-app-yaml", "ENV=HUGO_ENV=production"]
- name: "gcr.io/cloud-builders/gcloud" # deploy the website
dir: "site"
args: ["app", "deploy", ".app.yaml", "--promote", "--version=$SHORT_SHA"]
env:
- GOPATH=/workspace/go
- GO111MODULE=on
#
# Build development site and deploy
#
- name: "make-docker" # build a preview of the website
dir: "build"
args: ["site-static-preview", "site-gen-app-yaml", "SERVICE=development"]
- name: "gcr.io/cloud-builders/gcloud" # deploy the preview of the website
dir: "site"
args: ["app", "deploy", ".app.yaml", "--promote", "--version=$SHORT_SHA"]
env:
- GOPATH=/workspace/go
- GO111MODULE=on
tags: ["site"]

155
config.toml Normal file
View File

@ -0,0 +1,155 @@
# Copyright 2018 Google LLC All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
baseURL = "/"
title = "Redis Operator"
enableRobotsTXT = true
# Hugo allows theme composition (and inheritance). The precedence is from left to right.
theme = ["docsy"]
# Will give values to .Lastmod etc.
enableGitInfo = true
# Language settings
contentDir = "content/en"
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = false
# Useful when translating.
enableMissingTranslationPlaceholders = true
disableKinds = ["taxonomy", "taxonomyTerm"]
# Highlighting config
pygmentsCodeFences = true
pygmentsUseClasses = false
# Use the new Chroma Go highlighter in Hugo.
pygmentsUseClassic = false
#pygmentsOptions = "linenos=table"
# See https://help.farbox.com/pygments.html
pygmentsStyle = "tango"
# First one is picked as the Twitter card image if not set on page.
#images = ["images/project-illustration.png"]
# Configure how URLs look like per section.
[permalinks]
blog = "/:section/:year/:month/:day/:slug/"
[markup.goldmark.renderer]
unsafe = true
# Image processing configuration.
[imaging]
resampleFilter = "CatmullRom"
quality = 75
anchor = "smart"
[services]
[services.googleAnalytics]
# Comment out the next line to disable GA tracking. Also disables the feature described in [params.ui.feedback].
# id = "UA-132708785-1"
# Language configuration
[languages]
[languages.en]
title = "Redis Operator"
description = "Redis Operator is an operator to deploy and manage Redis setup."
languageName ="English"
# Weight used for sorting.
weight = 1
# Everything below this are Site Params
[params]
copyright = "Copyright Opstree Solutions"
github_repo = "https://github.com/ot-container-kit/redis-operator"
# Google Custom Search Engine ID. Remove or comment out to disable search.
gcs_engine_id = "016691298986124624340:x7qv2dywdao"
# current release branch. Never is rc.
release_branch = "release-1.27.0"
# the main version. Never is rc.
release_version = "0.12.0"
# shown for production
supported_k8s = "1.23"
aks_minor_supported_k8s = "8"
minikube_minor_supported_k8s = "9"
# shown in development (or the next version that will be supported)
dev_supported_k8s = "1.23"
dev_aks_minor_supported_k8s = "8"
dev_minikube_minor_supported_k8s = "9"
# example tag
example_image_tag = "gcr.io/agones-images/simple-game-server:0.14"
# Enable syntax highlighting and copy buttons on code blocks with Prism
prism_syntax_highlighting = true
# User interface configuration
[params.ui]
# Enable to show the side bar menu in its compact state.
sidebar_menu_compact = false
# Set to true to disable breadcrumb navigation.
breadcrumb_disable = false
# Adds a H2 section titled "Feedback" to the bottom of each doc. The responses are sent to Google Analytics as events.
# This feature depends on [services.googleAnalytics] and will be disabled if "services.googleAnalytics.id" is not set.
# If you want this feature, but occasionally need to remove the "Feedback" section from a single page,
# add "hide_feedback: true" to the page's front matter.
[params.ui.feedback]
enable = false
# The responses that the user sees after clicking "yes" (the page was helpful) or "no" (the page was not helpful).
[params.links]
# End user relevant links. These will show up on left side of footer and in the community page if you have one.
[[params.links.user]]
name = "Slack"
url = "https://join.slack.com/t/opstree/shared_invite/zt-3o8jp35x-UGMU2Cy0WSBk3Lbzqa2wVw"
icon = "fab fa-slack"
desc = "Chat with other project users in #users"
[[params.links.user]]
name = "User mailing list"
url = "https://github.com/OT-CONTAINER-KIT/redis-operator/discussions"
icon = "fa fa-envelope"
desc = "Discussion and help from your fellow users"
[[params.links.user]]
name ="Twitter"
url = "https://twitter.com/opstreedevops"
icon = "fab fa-twitter"
desc = "Follow us on Twitter to get the latest news!"
# Developer relevant links. These will show up on right side of footer and in the community page if you have one.
[[params.links.developer]]
name = "GitHub"
url = "https://github.com/OT-CONTAINER-KIT/redis-operator"
icon = "fab fa-github"
desc = "Development takes place here!"
[[params.links.developer]]
name = "Slack"
url = "https://join.slack.com/t/opstree/shared_invite/zt-3o8jp35x-UGMU2Cy0WSBk3Lbzqa2wVw"
icon = "fab fa-slack"
desc = "Chat with other project developers in #developers"
[security]
enableInlineShortcodes = false
[security.exec]
allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$']
osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM)$']
[security.funcs]
getenv = ['^HUGO_', 'RELEASE_BRANCH', 'RELEASE_VERSION']

46
content/en/_index.html Normal file
View File

@ -0,0 +1,46 @@
+++
title = "Redis Operator"
linkTitle = "Redis Operator"
+++
{{< blocks/cover title="" image_anchor="top" height="min" color="white" >}}
<div class="mx-auto">
<a class="btn btn-lg btn-secondary mr-3 mb-4" href="{{< relref "/docs" >}}">
Learn More <i class="fas fa-arrow-alt-circle-right ml-2"></i>
</a>
<a class="btn btn-lg btn-secondary mr-3 mb-4" href="https://github.com/ot-container-kit/redis-operator/releases">
Releases <i class="fab fa-github ml-2 "></i>
</a>
<p class="lead mt-4">Run Redis setup on any Kubernetes cluster</p>
<div class="mx-auto mt-5">
{{< blocks/link-down color="light-secondary" >}}
</div>
</div>
{{< /blocks/cover >}}
{{% blocks/lead color="secondary" %}}
<strong>What is Redis Operator?</strong>
A Golang based redis operator that will make/oversee Redis standalone and cluster mode setup on top of the Kubernetes.
It can create a redis cluster setup with best practices on Cloud as well as the Bare metal environment.
Also, it provides an in-built monitoring capability using redis-exporter.
{{% /blocks/lead %}}
{{< blocks/section color="secondary" >}}
{{% blocks/feature icon="fab fa-slack" title="Join us on Slack" url="https://join.slack.com/t/opstree/shared_invite/zt-3o8jp35x-UGMU2Cy0WSBk3Lbzqa2wVw" url_text="Join us" %}}
Join the community on Slack
{{% /blocks/feature %}}
{{% blocks/feature icon="fab fa-github" title="Contributions welcome!" url="https://github.com/OT-CONTAINER-KIT/redis-operator" url_text="Contribute to Redis Operator" %}}
We do a [Pull Request](https://github.com/OT-CONTAINER-KIT/redis-operator/pulls) contributions workflow on **GitHub**.
New users are always welcome!
{{% /blocks/feature %}}
{{% blocks/feature icon="fab fa-twitter" title="Follow us on Twitter!" url="https://twitter.com/opstreedevops" url_text="Follow us" %}}
For announcement of the latest features etc.
{{% /blocks/feature %}}
{{< /blocks/section >}}

View File

@ -0,0 +1,8 @@
---
title: Community
menu:
main:
weight: 40
---
<!--add blocks of content here to add more sections to the community page -->

View File

@ -0,0 +1,49 @@
---
title: "Exposing Redis Service"
linkTitle: "Exposing Redis Service"
weight: 10
date: 2022-11-02T00:19:19Z
description: >
Instructions for exposing redis outside Kubernetes cluster
---
By default, the nature of Redis standalone/cluster setup is private and limited to the Kubernetes cluster only. But we do have a provision to expose it using the Kubernetes "Service" object. If we can expose the service by doing some configuration inside the helm values for redis standalone and cluster setup. This will create another service in parallel to the internal redis service to expose redis.
The service can be exposed with these service types:
- **NodePort:** Exposes the Service on each Node's IP at a static port (the NodePort). A ClusterIP Service, to which the NodePort Service routes, is automatically created. You'll be able to contact the NodePort Service, from outside the cluster, by requesting <NodeIP>:<NodePort>.
- **LoadBalancer:** Exposes the Service externally using a cloud provider's load balancer. NodePort and ClusterIP Services, to which the external load balancer routes, are automatically created.
## Exposing Service
Customize or create the values file with the following content. The externalService configuration is a common method of exposing service for redis standalone and cluster setup:
```yaml
externalService:
enabled: true
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
serviceType: LoadBalancer
port: 6379
```
Once the values file is customized or created we can apply or upgrade the redis setup. We need to pass the created file as an argument to the `helm` command.
```shell
# redis standalone
$ helm upgrade redis ot-helm/redis -f custom-values.yaml \
--install --namespace ot-operators
# redis cluster
$ helm upgrade redis-cluster ot-helm/redis-cluster -f custom-values.yaml \
--set redisCluster.clusterSize=3 --install --namespace ot-operators
```
Once helm command is completed successfully, we can verify the external service by kubectl command. As we can see in the output, there is an IP in the "EXTERNAL-IP" coloumn.
```shell
$ kubectl get svc -n ot-operators
...
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis-external-service LoadBalancer 10.103.9.171 164.52.207.101 6379:32247/TCP,9121:30708/TCP 4d20h
```

View File

@ -0,0 +1,190 @@
---
title: "TLS and Security"
linkTitle: "TLS and Security"
weight: 30
date: 2022-11-02T00:19:19Z
description: >
Instructions for configuring TLS and security by Redis Operator
---
## Securing redis setup with password
If we want to use password based authentication inside Redis, we need to create a secret for it. By default, operator doesn't apply password on standalone or cluster. We need to enable the password login via `helm` command or `YAML` manifests.
```shell
$ kubectl create secret generic redis-secret \
--from-literal=password=password -n ot-operators
```
For users that are managing Redis setup using `YAML` manifest, they need to define `redisSecret` inside the object of Redis and RedisCluster. For further details, please check [here](../../crd-reference/redis-api/#existingpasswordsecret).
```yaml
spec:
kubernetesConfig:
redisSecret:
name: redis-secret
key: password
```
With `helm`, the password configuration can be defined inside the values file and also can be passed using `helm --set` command.
Password configuration for Redis standalone:
```shell
$ helm install redis ot-helm/redis --namespace ot-operators \
--set redisStandalone.redisSecret.secretName=redis-secret \
--set redisStandalone.redisSecret.secretKey=password
```
Password configuration for Redis cluster:
```shell
$ helm install redis ot-helm/redis-cluster --namespace ot-operators \
--set redisCluster.redisSecret.secretName=redis-secret \
--set redisCluster.redisSecret.secretKey=password
```
Once the password configuration is applied to the redis setup, we can perform the validation using the combination of `kubectl` and `redis-cli`.
```shell
$ kubectl exec -it redis-0 -n ot-operators -c redis -- redis-cli info
...
NOAUTH Authentication required.
```
Try passing the password to the redis setup:
```shell
$ kubectl exec -it redis-0 -n ot-operators -c redis -- redis-cli -a password info
...
# Server
redis_version:7.0.5
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:90d2ef529791ba03
redis_mode:standalone
os:Linux 5.4.209-116.367.amzn2.x86_64 x86_64
arch_bits:64
.......
```
## TLS configuration for redis setup
TLS is a security protocol that makes packet and network transfer encrypted between server and client architecture. In the redis setup, we can add TLS as a part of an additional security layer, and along with username and password, the TLS parameters also need to be passed to the client for server authentication.
The architecture with TLS setup looks like this:
<div align="center">
<img src="../../../images/redis-tls.png">
</div>
### Certificate creation
TLS certificates can be purchased or self-signed, but both can be integrated with the redis setup. In Kubernetes, we can install [cert-manager,](https://cert-manager.io/docs/) and it can be used for generating the certificates inside Kubernetes for redis and different other applications.
First, we need to create an issuer inside the Kubernetes to issue the certificates for redis TLS integration.
```yaml
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: redis-tls-ca
spec:
ca:
secretName: redis-tls-ca-cert
```
```shell
$ kubectl apply -f issuer.yaml -n ot-operators
...
issuer.cert-manager.io/redis-tls-ca created
```
Verify the issuer by using `kubectl` command:
```shell
$ kubectl get issuers -n ot-operators
```
Once the issuer configuration is done, we need to create a `Certificate` object to create certificate for Redis.
```yaml
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: redis-tls-ca
spec:
isCA: true
commonName: redis
secretName: redis-tls-ca-cert
issuerRef:
name: selfsigned-issuer
kind: Issuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: redis-tls # this name should match the one appeared in kustomizeconfig.yaml
spec:
dnsNames:
- redis-headless.ot-operators.svc.cluster.local
- redis-headless.ot-operators.svc
- redis-headless
issuerRef:
kind: Issuer
name: redis-tls-ca
group: cert-manager.io
secretName: redis-tls-cert
```
Create the defined `YAML` object inside the Kubernetes cluster using `kubectl` command:
```shell
$ kubectl apply -f certificate.yaml -n ot-operators
...
certificate.cert-manager.io/redis-tls-ca created
certificate.cert-manager.io/redis-tls created
```
Once the certificates are in ready, we can verify if the TLS secret is created or not by cert-manager.
```shell
$ kubectl get certificates -n ot-operators
...
NAME READY SECRET AGE
redis-tls True redis-tls-cert 108s
redis-tls-ca True redis-tls-ca-cert 109s
```
```shell
$ kubectl get secrets -n ot-operators
...
NAME TYPE DATA AGE
redis-tls-cert kubernetes.io/tls 3 18m
```
### Redis TLS configuration
Redis TLS configuration can be done via `YAML` manifests and `helm` values file. We need to add details about the certificate to the Redis and RedisCluster objects.
For `YAML` manifest configuration, we need to define the TLS block like this:
```yaml
spec:
TLS:
secret:
secretName: redis-tls-cert
optional: false
```
For `helm upgrade` method we need to update the values file of `Redis` and `RedisCluster`.

View File

@ -0,0 +1,172 @@
---
title: "Upgrading Redis and RedisCluster"
linkTitle: "Upgrading Redis and RedisCluster"
weight: 20
date: 2022-11-02T00:19:19Z
description: >
Instructions for upgrading the redis and redis cluster
---
The upgrade strategy for standalone Redis includes the downtime of the system but for cluster setup mode any application will not face any type of issues or downtime. The operator uses the rolling deployment strategy for cluster setup where it will upgrade the redis leader pods and once the leader is upgraded it will follow the same strategy for redis follower pods.
## Upgrade of standalone setup
For upgrading the standalone setup of Redis, first we need to identify the current version of it and that can be done via combination of `kubectl` and `redis-server` command.
```shell
$ kubectl exec -it redis-0 -n ot-operators -c redis -- redis-server --version
...
Redis server v=6.2.5 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=3e393a4e624651a3
```
Now let's say the new version, we want to migrate is `7.0.5`. In that case, we can simply use `helm upgrade` command to upgrade the redis cluster.
```shell
$ helm upgrade redis ot-helm/redis --namespace ot-operators \
--set redisStandalone.tag=v7.0.5
...
Release "redis" has been upgraded. Happy Helming!
NAME: redis
LAST DEPLOYED: Wed Nov 2 19:58:42 2022
NAMESPACE: ot-operators
STATUS: deployed
REVISION: 2
TEST SUITE: None
```
Once the upgrade strategy is completed, we can verify the redis pod status by executing:
```shell
$ kubectl get pods -n ot-operators
...
NAME READY STATUS RESTARTS AGE
redis-0 2/2 Running 0 6m26s
```
Verify the version of redis pod by using the same cli command.
```shell
$ kubectl exec -it redis-0 -n ot-operators -c redis -- redis-server --version
...
Redis server v=7.0.5 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=90d2ef529791ba03
```
For YAML manifest based upgrade, please update the `spec` section of Redis Object. For further details check [here](../../crd-reference/redis-api/#kubernetesconfig).
```yaml
spec:
kubernetesConfig:
image: "quay.io/opstree/redis:v7.0.5"
imagePullPolicy: "IfNotPresent"
```
**Things to keep in mind:**
{{< alert color="info" title="Note" >}}
- Standalone upgrade introduces downtime, so make sure you have strategy in-place. A better option could be deploying a new redis standalone alongside and point the application to it.
- Your applications should be compatible with the new version of Redis on which you are upgrading.
{{< /alert >}}
## Upgrading cluster setup
Similar to standalone setup upgrade, the cluster setup upgrade can also be performed by `helm` command. But again first we need to verify the version of existing cluster setup.
```shell
$ kubectl get pods -n ot-operators
...
NAME READY STATUS RESTARTS AGE
redis-cluster-follower-0 2/2 Running 0 2m22s
redis-cluster-follower-1 2/2 Running 0 2m9s
redis-cluster-follower-2 2/2 Running 0 102s
redis-cluster-leader-0 2/2 Running 0 2m22s
redis-cluster-leader-1 2/2 Running 0 2m9s
redis-cluster-leader-2 2/2 Running 0 101s
```
As an initial step of Redis cluster upgrade, first we need to check the version of `redis` and also the health of cluster. Again this can be achieved by `kubectl` and `redis-cli`.
```shell
$ kubectl exec -it redis-cluster-leader-0 -c redis-cluster-leader \
-n ot-operators -- redis-server --version
...
Redis server v=6.2.5 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=3e393a4e624651a3
```
```shell
$ kubectl exec -it redis-cluster-leader-0 -c redis-cluster-leader \
-n ot-operators -- redis-cli cluster nodes
...
be337e56dbcbdcf47af569e53a0f0316f8e5cd28 192.168.94.6:6379@16379 slave af958687b0048c734b13cef5632ab2b46e386fb1 0 1667400526029 3 connected
2321a671fba363fe55821cd133d4b70fbdeba713 192.168.3.127:6379@16379 myself,master - 0 1667400527000 1 connected 0-5460
93eb534f0e748b04ff4be6fe984173cdc65703eb 192.168.37.210:6379@16379 slave 670c7c36d3d89c93d4bc546e6ee02b2b843d8801 0 1667400527536 2 connected
a00493797d566f2cb1f861962e94fee453d23857 192.168.2.131:6379@16379 slave 2321a671fba363fe55821cd133d4b70fbdeba713 0 1667400527033 1 connected
670c7c36d3d89c93d4bc546e6ee02b2b843d8801 192.168.33.178:6379@16379 master - 0 1667400526000 2 connected 5461-10922
af958687b0048c734b13cef5632ab2b46e386fb1 192.168.72.57:6379@16379 master - 0 1667400527000 3 connected 10923-16383
```
Once the version and cluster health is verified, we can trigger the upgrade of the cluster by using `helm` command. Let's upgrade the cluster version to v7.
```shell
$ helm upgrade redis-cluster ot-helm/redis-cluster \
--set redisCluster.clusterSize=3 --install --namespace ot-operators \
--set redisCluster.tag=v7.0.5 --set redisCluster.clusterVersion=v7
...
Release "redis-cluster" has been upgraded. Happy Helming!
NAME: redis-cluster
LAST DEPLOYED: Wed Nov 2 20:21:35 2022
NAMESPACE: ot-operators
STATUS: deployed
REVISION: 2
TEST SUITE: None
```
Once the upgrade using helm is completed, we need to verify the redis cluster pod status. Also, we need to check the version and health of redis cluster to verify the upgrade is successful or not.
```shell
$ kubectl get pods -n ot-operators
...
NAME READY STATUS RESTARTS AGE
redis-cluster-follower-0 2/2 Running 0 78s
redis-cluster-follower-1 2/2 Running 0 2m5s
redis-cluster-follower-2 2/2 Running 0 2m49s
redis-cluster-leader-0 2/2 Running 0 77s
redis-cluster-leader-1 2/2 Running 0 2m4s
redis-cluster-leader-2 2/2 Running 0 2m50s
```
```shell
$ kubectl exec -it redis-cluster-leader-0 -c redis-cluster-leader \
-n ot-operators -- redis-server --version
...
Redis server v=7.0.5 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=90d2ef529791ba03
```
```shell
$ kubectl exec -it redis-cluster-leader-0 -c redis-cluster-leader \
-n ot-operators -- redis-cli cluster nodes
...
af958687b0048c734b13cef5632ab2b46e386fb1 192.168.90.191:6379@16379 master - 0 1667400984185 3 connected 10923-16383
be337e56dbcbdcf47af569e53a0f0316f8e5cd28 192.168.65.215:6379@16379 slave af958687b0048c734b13cef5632ab2b46e386fb1 0 1667400983000 3 connected
a00493797d566f2cb1f861962e94fee453d23857 192.168.8.246:6379@16379 slave 2321a671fba363fe55821cd133d4b70fbdeba713 0 1667400984184 1 connected
670c7c36d3d89c93d4bc546e6ee02b2b843d8801 192.168.32.65:6379@16379 master - 0 1667400983180 2 connected 5461-10922
2321a671fba363fe55821cd133d4b70fbdeba713 192.168.2.134:6379@16379 myself,master - 0 1667400983000 1 connected 0-5460
93eb534f0e748b04ff4be6fe984173cdc65703eb 192.168.52.229:6379@16379 slave 670c7c36d3d89c93d4bc546e6ee02b2b843d8801 0 1667400984687 2 connected
```
For YAML manifest based upgrade, please update the `spec` section of Redis Object. For further details check [here](../../crd-reference/redis-api/#kubernetesconfig).
```yaml
spec:
kubernetesConfig:
image: "quay.io/opstree/redis:v7.0.5"
imagePullPolicy: "IfNotPresent"
```
**Things to keep in mind:**
{{< alert color="info" title="Note" >}}
- Cluster upgrade doesn't cause any kind of downtime because of rolling update strategy of Kubernetes, there will be always a redis available to serve application requests.
- If application is highly critical, in such scenarios it would make sense to create a new cluster and migrate the application pointing to it
{{< /alert >}}

View File

@ -0,0 +1,8 @@
---
title: "Advance Configuration"
linkTitle: "Advance Configuration"
weight: 7
date: 2022-11-02T00:19:19Z
description: >
Instructions for advance configuration and use-cases for Redis Operator
---

View File

@ -0,0 +1,377 @@
---
title: "Custom Resource Object API"
linkTitle: "Custom Resource Object API"
weight: 10
date: 2022-11-02T00:19:19Z
description: >
CRD Schema details for Redis and Redis Cluster Reference API
---
# API Reference
## Packages
- [redis.redis.opstreelabs.in/v1beta1](#redisredisopstreelabsinv1beta1)
## redis.redis.opstreelabs.in/v1beta1
Package v1beta1 contains API Schema definitions for the redis v1beta1 API group
### Resource Types
- [Redis](#redis)
- [RedisCluster](#rediscluster)
- [RedisReplication](#redisreplication)
- [RedisSentinel](#redissentinel)
#### ExistingPasswordSecret
ExistingPasswordSecret is the struct to access the existing secret
_Appears in:_
- [KubernetesConfig](#kubernetesconfig)
| Field | Description |
| --- | --- |
| `name` _string_ | |
| `key` _string_ | |
#### KubernetesConfig
KubernetesConfig will be the JSON struct for Basic Redis Config
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinel](#redissentinelspec)
| Field | Description |
| --- | --- |
| `image` _string_ | |
| `imagePullPolicy` _[PullPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#pullpolicy-v1-core)_ | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#resourcerequirements-v1-core)_ | |
| `redisSecret` _[ExistingPasswordSecret](#existingpasswordsecret)_ | |
| `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#localobjectreference-v1-core)_ | |
| `updateStrategy` _[StatefulSetUpdateStrategy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#statefulsetupdatestrategy-v1-apps)_ | |
#### Probe
Probe is a interface for ReadinessProbe and LivenessProbe
_Appears in:_
- [RedisFollower](#redisfollower)
- [RedisLeader](#redisleader)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinel](#redissentinelspec)
| Field | Description |
| --- | --- |
| `initialDelaySeconds` _integer_ | |
| `timeoutSeconds` _integer_ | |
| `periodSeconds` _integer_ | |
| `successThreshold` _integer_ | |
| `failureThreshold` _integer_ | |
#### Redis
Redis is the Schema for the redis API
| Field | Description |
| --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta1`
| `kind` _string_ | `Redis`
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |
| `spec` _[RedisSpec](#redisspec)_ | |
#### RedisCluster
RedisCluster is the Schema for the redisclusters API
| Field | Description |
| --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta1`
| `kind` _string_ | `RedisCluster`
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |
| `spec` _[RedisClusterSpec](#redisclusterspec)_ | |
#### RedisReplication
RedisReplication is the Schema for the redisreplication API
| Field | Description |
| --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta1`
| `kind` _string_ | `RedisReplication`
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |
| `spec` _[RedisReplicationSpec](#redisreplicationspec)_ | |
#### RedisSentinel
RedisSentinel is the Schema for the redissentinel API
| Field | Description |
| --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta1`
| `kind` _string_ | `RedisSentinel`
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |
| `spec` _[RedisSentinelSpec](#redissentinelspec)_ | |
#### RedisClusterSpec
RedisClusterSpec defines the desired state of RedisCluster
_Appears in:_
- [RedisCluster](#rediscluster)
| Field | Description |
| --- | --- |
| `clusterSize` _integer_ | |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | |
| `clusterVersion` _string_ | |
| `redisLeader` _[RedisLeader](#redisleader)_ | |
| `redisFollower` _[RedisFollower](#redisfollower)_ | |
| `redisExporter` _[RedisExporter](#redisexporter)_ | |
| `storage` _[Storage](#storage)_ | |
| `nodeSelector` _object (keys:string, values:string)_ | |
| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core)_ | |
| `priorityClassName` _string_ | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core)_ | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#resourcerequirements-v1-core)_ | |
| `TLS` _[TLSConfig](#tlsconfig)_ | |
| `sidecars` _[Sidecar](#sidecar)_ | |
| `serviceAccountName` _string_ | |
| `persistenceEnabled` _boolean_ | |
#### RedisSpec
RedisSpec defines the desired state of Redis
_Appears in:_
- [Redis](#redis)
| Field | Description |
| --- | --- |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | |
| `redisExporter` _[RedisExporter](#redisexporter)_ | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | |
| `storage` _[Storage](#storage)_ | |
| `nodeSelector` _object (keys:string, values:string)_ | |
| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core)_ | |
| `priorityClassName` _string_ | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core)_ | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core)_ | |
| `TLS` _[TLSConfig](#tlsconfig)_ | |
| `readinessProbe` _[Probe](#probe)_ | |
| `livenessProbe` _[Probe](#probe)_ | |
| `sidecars` _[Sidecar](#sidecar)_ | |
| `serviceAccountName` _string_ | |
#### RedisReplicationSpec
RedisReplicationSpec defines the desired state of RedisReplication
_Appears in:_
- [RedisReplication](#redisreplication)
| Field | Description |
| --- | --- |
| `clusterSize` _integer_ | |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | |
| `redisExporter` _[RedisExporter](#redisexporter)_ | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | |
| `storage` _[Storage](#storage)_ | |
| `nodeSelector` _object (keys:string, values:string)_ | |
| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core)_ | |
| `priorityClassName` _string_ | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core)_ | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core)_ | |
| `TLS` _[TLSConfig](#tlsconfig)_ | |
| `readinessProbe` _[Probe](#probe)_ | |
| `livenessProbe` _[Probe](#probe)_ | |
| `sidecars` _[Sidecar](#sidecar)_ | |
| `serviceAccountName` _string_ | |
#### RedisSentinelSpec
RedisSentinelSpec defines the desired state of RedisSentinel
_Appears in:_
- [RedisSentinel](#redissentinel)
| Field | Description |
| --- | --- |
| `clusterSize` _integer_ | |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | |
| `redisSentinelConfig` _[RedisSentinelConfig](#redissentinelconfig)_ | |
| `nodeSelector` _object (keys:string, values:string)_ | |
| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core)_ | |
| `priorityClassName` _string_ | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core)_ | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core)_ | |
| `TLS` _[TLSConfig](#tlsconfig)_ | |
| `readinessProbe` _[Probe](#probe)_ | |
| `livenessProbe` _[Probe](#probe)_ | |
| `sidecars` _[Sidecar](#sidecar)_ | |
| `serviceAccountName` _string_ | |
#### RedisConfig
RedisConfig defines the external configuration of Redis
_Appears in:_
- [RedisFollower](#redisfollower)
- [RedisLeader](#redisleader)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
| Field | Description |
| --- | --- |
| `additionalRedisConfig` _string_ | |
#### RedisSentinelConfig
RedisSentinelConfig defines the external configuration of RedisSentinel
_Appears in:_
- [RedisSentinelSpec](#redissentinelspec)
| Field | Description |
| --- | --- |
| `additionalRedisConfig` _string_ | |
| `masterGroupName` _string_ | |
| `redisPort` _string_ | |
| `quorum` _string_ | |
| `parallelSyncs` _string_ | |
| `failoverTimeout` _string_ | |
| `downAfterMilliseconds` _string_ | |
#### RedisExporter
RedisExporter interface will have the information for redis exporter related stuff
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
| Field | Description |
| --- | --- |
| `enabled` _boolean_ | |
| `image` _string_ | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#resourcerequirements-v1-core)_ | |
| `imagePullPolicy` _[PullPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#pullpolicy-v1-core)_ | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#envvar-v1-core)_ | |
#### RedisFollower
RedisFollower interface will have the redis follower configuration
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
| Field | Description |
| --- | --- |
| `replicas` _integer_ | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core)_ | |
| `pdb` _[RedisPodDisruptionBudget](#redispoddisruptionbudget)_ | |
| `readinessProbe` _[Probe](#probe)_ | |
| `livenessProbe` _[Probe](#probe)_ | |
#### RedisLeader
RedisLeader interface will have the redis leader configuration
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
| Field | Description |
| --- | --- |
| `replicas` _integer_ | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core)_ | |
| `pdb` _[RedisPodDisruptionBudget](#redispoddisruptionbudget)_ | |
| `readinessProbe` _[Probe](#probe)_ | |
| `livenessProbe` _[Probe](#probe)_ | |
#### RedisPodDisruptionBudget
RedisPodDisruptionBudget configure a PodDisruptionBudget on the resource (leader/follower)
_Appears in:_
- [RedisFollower](#redisfollower)
- [RedisLeader](#redisleader)
- [RedisReplication](#redisreplicationspec)
- [RedisSentinel](#redissentinelspec)
| Field | Description |
| --- | --- |
| `enabled` _boolean_ | |
| `minAvailable` _integer_ | |
| `maxUnavailable` _integer_ | |
#### Sidecar
Sidecar for each Redis pods
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinel](#redissentinelspec)
| Field | Description |
| --- | --- |
| `name` _string_ | |
| `image` _string_ | |
| `imagePullPolicy` _[PullPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#pullpolicy-v1-core)_ | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#resourcerequirements-v1-core)_ | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#envvar-v1-core)_ | |
#### Storage
Storage is the inteface to add pvc and pv support in redis
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
| Field | Description |
| --- | --- |
| `volumeClaimTemplate` _[PersistentVolumeClaim](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#persistentvolumeclaim-v1-core)_ | |
#### TLSConfig
TLS Configuration for redis instances
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinel](#redissentinelspec)
| Field | Description |
| --- | --- |
| `ca` _string_ | |
| `cert` _string_ | |
| `key` _string_ | |
| `secret` _[SecretVolumeSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#secretvolumesource-v1-core)_ | Reference to secret which contains the certificates |

View File

@ -0,0 +1,14 @@
---
title: "CRD Reference"
linkTitle: "CRD Reference"
weight: 6
date: 2022-11-02T00:19:19Z
description: >
Reference documentation for CRD spec of Redis and Redis cluster
---
A resource in the Kubernetes API is an endpoint that persists a collection of a particular type of object. For example, several built-in objects, like pods and deployments, are exposed via an endpoint, and the API server manages their lifecycle. Kubernetes provides you with an option of extending your object using CRD so that you can introduce your API to the Kubernetes cluster per your requirement. Using CRD on Kubernetes, you are free to define, create, and persist any custom object.
<div align="center">
<img src="../../../images/crd-architecture.png">
</div>

View File

@ -0,0 +1,42 @@
---
title: "Redis"
linkTitle: "Redis"
weight: 10
date: 2022-11-02T00:19:19Z
description: >
Configurations and parameters for Redis standalone
---
Redis standalone configuration can be customized by [values.yaml](https://github.com/OT-CONTAINER-KIT/helm-charts/blob/main/charts/redis/values.yaml). The recommended way of managing the setup is using `helm` but if the setup is not maintained by it, `YAML` CRD parameters can be modified in the manifest.
## Helm Configuration Parameters
| **Name** | **Value** | **Description** |
|-----------------------------------|--------------------------------|-----------------------------------------------------------------------------------------------|
| `imagePullSecrets` | [] | List of image pull secrets, in case redis image is getting pull from private registry |
| `redisStandalone.secretName` | redis-secret | Name of the existing secret in Kubernetes |
| `redisStandalone.secretKey` | password | Name of the existing secret key in Kubernetes |
| `redisStandalone.image` | quay.io/opstree/redis | Name of the redis image |
| `redisStandalone.tag` | v7.0.5 | Tag of the redis image |
| `redisStandalone.imagePullPolicy` | IfNotPresent | Image Pull Policy of the redis image |
| `redisStandalone.resources` | {} | Request and limits for redis statefulset |
| `externalService.enabled` | false | If redis service needs to be exposed using LoadBalancer or NodePort |
| `externalService.annotations` | {} | Kubernetes service related annotations |
| `externalService.serviceType` | NodePort | Kubernetes service type for exposing service, values - ClusterIP, NodePort, and LoadBalancer |
| `externalService.port` | 6379 | Port number on which redis external service should be exposed |
| `serviceMonitor.enabled` | false | Servicemonitor to monitor redis with Prometheus |
| `serviceMonitor.interval` | 30s | Interval at which metrics should be scraped. |
| `serviceMonitor.scrapeTimeout` | 10s | Timeout after which the scrape is ended |
| `serviceMonitor.namespace` | monitoring | Namespace in which Prometheus operator is running |
| `redisExporter.enabled` | true | Redis exporter should be deployed or not |
| `redisExporter.image` | quay.io/opstree/redis-exporter | Name of the redis exporter image |
| `redisExporter.tag` | v1.44.0 | Tag of the redis exporter image |
| `redisExporter.imagePullPolicy` | IfNotPresent | Image Pull Policy of the redis exporter image |
| `redisExporter.env` | [] | Extra environment variables which needs to be added in redis exporter |
| `nodeSelector` | {} | NodeSelector for redis statefulset |
| `priorityClassName` | "" | Priority class name for the redis statefulset |
| `storageSpec` | {} | Storage configuration for redis setup |
| `securityContext` | {} | Security Context for redis pods for changing system or kernel level parameters |
| `affinity` | {} | Affinity for node and pod for redis statefulset |
| `tolerations` | [] | Tolerations for redis statefulset |
| `sidecars` | [] | Sidecar containers to run alongside Redis pods |

View File

@ -0,0 +1,46 @@
---
title: "RedisCluster"
linkTitle: "RedisCluster"
weight: 20
date: 2022-11-02T00:19:19Z
description: >
Configurations and parameters for Redis cluster
---
Redis cluster can be customized by [values.yaml](https://github.com/OT-CONTAINER-KIT/helm-charts/blob/main/charts/redis-cluster/values.yaml). The recommended way of managing the setup is using `helm` but if the setup is not maintained by it, `YAML` CRD parameters can be modified in the manifest.
## Helm Configuration Parameters
| **Name** | **Default Value** | **Description** |
|------------------------------------|--------------------------------|----------------------------------------------------------------------------------------------|
| `imagePullSecrets` | [] | List of image pull secrets, in case redis image is getting pull from private registry |
| `redisCluster.clusterSize` | 3 | Size of the redis cluster leader and follower nodes |
| `redisCluster.clusterVersion` | v7 | Major version of Redis setup, values can be v6 or v7 |
| `redisCluster.persistenceEnabled` | true | Persistence should be enabled or not in the Redis cluster setup |
| `redisCluster.secretName` | redis-secret | Name of the existing secret in Kubernetes |
| `redisCluster.secretKey` | password | Name of the existing secret key in Kubernetes |
| `redisCluster.image` | quay.io/opstree/redis | Name of the redis image |
| `redisCluster.tag` | v7.0.5 | Tag of the redis image |
| `redisCluster.imagePullPolicy` | IfNotPresent | Image Pull Policy of the redis image |
| `redisCluster.leaderServiceType` | ClusterIP | Kubernetes service type for Redis Leader |
| `redisCluster.followerServiceType` | ClusterIP | Kubernetes service type for Redis Follower |
| `externalService.enabled` | false | If redis service needs to be exposed using LoadBalancer or NodePort |
| `externalService.annotations` | {} | Kubernetes service related annotations |
| `externalService.serviceType` | NodePort | Kubernetes service type for exposing service, values - ClusterIP, NodePort, and LoadBalancer |
| `externalService.port` | 6379 | Port number on which redis external service should be exposed |
| `serviceMonitor.enabled` | false | Servicemonitor to monitor redis with Prometheus |
| `serviceMonitor.interval` | 30s | Interval at which metrics should be scraped. |
| `serviceMonitor.scrapeTimeout` | 10s | Timeout after which the scrape is ended |
| `serviceMonitor.namespace` | monitoring | Namespace in which Prometheus operator is running |
| `redisExporter.enabled` | true | Redis exporter should be deployed or not |
| `redisExporter.image` | quay.io/opstree/redis-exporter | Name of the redis exporter image |
| `redisExporter.tag` | v1.44.0 | Tag of the redis exporter image |
| `redisExporter.imagePullPolicy` | IfNotPresent | Image Pull Policy of the redis exporter image |
| `redisExporter.env` | [] | Extra environment variables which needs to be added in redis exporter |
| `sidecars` | [] | Sidecar container to run alongside Redis pods |
| `nodeSelector` | {} | NodeSelector for redis statefulset |
| `priorityClassName` | "" | Priority class name for the redis statefulset |
| `storageSpec` | {} | Storage configuration for redis setup |
| `securityContext` | {} | Security Context for redis pods for changing system or kernel level parameters |
| `affinity` | {} | Affinity for node and pods for redis statefulset |
| `tolerations` | [] | Tolerations for redis statefulset management |

View File

@ -0,0 +1,42 @@
---
title: "RedisReplication"
linkTitle: "RedisReplication"
weight: 10
date: 2023-04-05T19:00:00Z
description: >
Configurations and parameters for Redis replication
---
Redis replication configuration can be customized by [values.yaml](https://github.com/OT-CONTAINER-KIT/helm-charts/blob/main/charts/redis-replication/values.yaml). The recommended way of managing the setup is using `helm` but if the setup is not maintained by it, `YAML` CRD parameters can be modified in the manifest.
## Helm Configuration Parameters
| **Name** | **Value** | **Description** |
|-----------------------------------|--------------------------------|-----------------------------------------------------------------------------------------------|
| `imagePullSecrets` | [] | List of image pull secrets, in case redis image is getting pull from private registry |
| `redisReplication.secretName` | redis-secret | Name of the existing secret in Kubernetes |
| `redisReplication.secretKey` | password | Name of the existing secret key in Kubernetes |
| `redisReplication.image` | quay.io/opstree/redis | Name of the redis image |
| `redisReplication.tag` | v7.0.5 | Tag of the redis image |
| `redisReplication.imagePullPolicy` | IfNotPresent | Image Pull Policy of the redis image |
| `redisReplication.resources` | {} | Request and limits for redis statefulset |
| `externalService.enabled` | false | If redis service needs to be exposed using LoadBalancer or NodePort |
| `externalService.annotations` | {} | Kubernetes service related annotations |
| `externalService.serviceType` | NodePort | Kubernetes service type for exposing service, values - ClusterIP, NodePort, and LoadBalancer |
| `externalService.port` | 6379 | Port number on which redis external service should be exposed |
| `serviceMonitor.enabled` | false | Servicemonitor to monitor redis with Prometheus |
| `serviceMonitor.interval` | 30s | Interval at which metrics should be scraped. |
| `serviceMonitor.scrapeTimeout` | 10s | Timeout after which the scrape is ended |
| `serviceMonitor.namespace` | monitoring | Namespace in which Prometheus operator is running |
| `redisExporter.enabled` | true | Redis exporter should be deployed or not |
| `redisExporter.image` | quay.io/opstree/redis-exporter | Name of the redis exporter image |
| `redisExporter.tag` | v1.44.0 | Tag of the redis exporter image |
| `redisExporter.imagePullPolicy` | IfNotPresent | Image Pull Policy of the redis exporter image |
| `redisExporter.env` | [] | Extra environment variables which needs to be added in redis exporter |
| `nodeSelector` | {} | NodeSelector for redis statefulset |
| `priorityClassName` | "" | Priority class name for the redis statefulset |
| `storageSpec` | {} | Storage configuration for redis setup |
| `securityContext` | {} | Security Context for redis pods for changing system or kernel level parameters |
| `affinity` | {} | Affinity for node and pod for redis statefulset |
| `tolerations` | [] | Tolerations for redis statefulset |
| `sidecars` | [] | Sidecar containers to run alongside Redis pods |

View File

@ -0,0 +1,8 @@
---
title: "Configuration"
linkTitle: "Configuration"
weight: 5
date: 2022-11-02T00:19:19Z
description: >
Instructions for configuration of Redis standalone and cluster setup.
---

View File

@ -0,0 +1,181 @@
---
title: "Contribute"
linkTitle: "Contribute"
weight: 9
date: 2022-11-02T00:19:19Z
description: >
How to contribute to the Redis Operator
---
## Prerequisites
- [Kubernetes Cluster](https://kubernetes.io)
- [Git](https://git-scm.com/downloads)
- [Go](https://golang.org/dl/)
- [Docker](https://docs.docker.com/install/)
- [Operator SDK](https://github.com/operator-framework/operator-sdk/releases)
- [Make](https://www.gnu.org/software/make/manual/make.html)
- [Eksctl](https://eksctl.io/)
## Local Kubernetes Cluster
For development and testing of operator on local system, we need to set up a [Minikube](https://minikube.sigs.k8s.io/docs/start/) or local Kubernetes cluster.
Minikube is a single node Kubernetes cluster that generally gets used for the development and testing on Kubernetes. For creating a Minkube cluster we need to simply run:
```shell
$ minikube start --vm-driver virtualbox
...
😄 minikube v1.0.1 on linux (amd64)
🤹 Downloading Kubernetes v1.14.1 images in the background ...
🔥 Creating kvm2 VM (CPUs=2, Memory=2048MB, Disk=20000MB) ...
📶 "minikube" IP address is 192.168.39.240
🐳 Configuring Docker as the container runtime ...
🐳 Version of container runtime is 18.06.3-ce
⌛ Waiting for image downloads to complete ...
✨ Preparing Kubernetes environment ...
🚜 Pulling images required by Kubernetes v1.14.1 ...
🚀 Launching Kubernetes v1.14.1 using kubeadm ...
⌛ Waiting for pods: apiserver proxy etcd scheduler controller dns
🔑 Configuring cluster permissions ...
🤔 Verifying component health .....
💗 kubectl is now configured to use "minikube"
🏄 Done! Thank you for using minikube!
```
## Cloud Kubernetes Cluster
For cloud based Kubernetes cluster we can use any type of platforms like [Amazon Web Service](https://aws.amazon.com/), [Azure Cloud](https://azure.microsoft.com/en-in/), or [Google Cloud Platform](https://cloud.google.com/). We have provided an [eks-cluster.yaml](./example/eks-cluster.yaml) file for creating an Elastic Kubernetes Service(EKS) using [eksctl](https://eksctl.io/).
`eksctl` is a cli tool to create a Kubernetes cluster on EKS by a single command. It supports creation of Ipv4 and Ipv6 based Kubernetes clusters for development.
```shell
$ eksctl create cluster -f example/eks-cluster.yaml
...
2022-10-30 19:47:44 [] eksctl version 0.114.0
2022-10-30 19:47:44 [] using region us-west-2
2022-10-30 19:47:45 [] setting availability zones to [us-west-2d us-west-2c us-west-2a]
2022-10-30 19:47:45 [] subnets for us-west-2d - public:192.168.0.0/19 private:192.168.96.0/19
2022-10-30 19:47:45 [] subnets for us-west-2c - public:192.168.32.0/19 private:192.168.128.0/19
2022-10-30 19:47:45 [] subnets for us-west-2a - public:192.168.64.0/19 private:192.168.160.0/19
2022-10-30 19:47:45 [] nodegroup "ng-1" will use "" [AmazonLinux2/1.22]
2022-10-30 19:47:45 [] using SSH public key "/Users/abhishekdubey/.ssh/id_rsa.pub" as "eksctl-operator-testing-nodegroup-ng-1-8b:2b:b2:fc:4c:7f:9c:0d:54:14:70:39:25:b5:6d:60"
2022-10-30 19:47:47 [] using Kubernetes version 1.22
2022-10-30 19:47:47 [] creating EKS cluster "operator-testing" in "us-west-2" region with managed nodes
2022-10-30 19:47:47 [] 1 nodegroup (ng-1) was included (based on the include/exclude rules)
2022-10-30 19:47:47 [] will create a CloudFormation stack for cluster itself and 0 nodegroup stack(s)
2022-10-30 19:47:47 [] will create a CloudFormation stack for cluster itself and 1 managed nodegroup stack(s)
2022-10-30 19:47:47 [] if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=us-west-2 --cluster=operator-testing'
2022-10-30 19:47:47 [] Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "operator-testing" in "us-west-2"
2022-10-30 19:47:47 [] CloudWatch logging will not be enabled for cluster "operator-testing" in "us-west-2"
2022-10-30 19:47:47 [] you can enable it with 'eksctl utils update-cluster-logging --enable-types={SPECIFY-YOUR-LOG-TYPES-HERE (e.g. all)} --region=us-west-2 --cluster=operator-testing'
2022-10-30 19:47:47 []
2 sequential tasks: { create cluster control plane "operator-testing",
2 sequential sub-tasks: {
5 sequential sub-tasks: {
wait for control plane to become ready,
associate IAM OIDC provider,
no tasks,
restart daemonset "kube-system/aws-node",
1 task: { create addons },
},
create managed nodegroup "ng-1",
}
}
2022-10-30 19:47:47 [] building cluster stack "eksctl-operator-testing-cluster"
2022-10-30 19:47:50 [] deploying stack "eksctl-operator-testing-cluster"
2022-10-30 20:01:17 [] daemonset "kube-system/aws-node" restarted
2022-10-30 20:01:18 [] creating role using recommended policies
2022-10-30 20:01:20 [] deploying stack "eksctl-operator-testing-addon-vpc-cni"
2022-10-30 20:01:20 [] waiting for CloudFormation stack "eksctl-operator-testing-addon-vpc-cni"
2022-10-30 20:01:52 [] waiting for CloudFormation stack "eksctl-operator-testing-addon-vpc-cni"
2022-10-30 20:02:24 [] waiting for CloudFormation stack "eksctl-operator-testing-addon-vpc-cni"
2022-10-30 20:02:26 [] creating addon
2022-10-30 20:02:37 [] addon "vpc-cni" active
2022-10-30 20:02:39 [] building managed nodegroup stack "eksctl-operator-testing-nodegroup-ng-1"
```
For setting up the Ipv4 or Ipv6 cluster with eksctl, we need to modify this configuration in the [eks-cluster.yaml](./example/eks-cluster.yaml):
```yaml
kubernetesNetworkConfig:
ipFamily: IPv4
# ipFamily: IPv6
```
## Operator structure
The structure for Redis operator includes different module's directory. The codebase include these major directories:
```shell
redis-operator/
|-- api
| |-- v1beta1
|-- bin
|-- config
| |-- certmanager
| |-- crd
| | |-- bases
| | |-- patches
| |-- default
| |-- manager
| |-- prometheus
| |-- rbac
| |-- samples
| |-- scorecard
| |-- bases
| |-- patches
|-- controllers
|-- hack
|-- k8sutils
```
As part of the development, generally, we modify the codebase in API, controllers, and k8sutils. The API modules hold the interface and structure for CRD definition, the controllers are the watch controllers that create, update, and delete the resources. The k8sutils is a module in which all the Kubernetes resources(Statefulsets, Services, etc.) codebase is present.
### Building Operator
For building operator, we can execute make command to create binary and docker image:
```shell
$ make manager
$ make docker-build
```
For any change inside the `api` module, we need to recreate the CRD schema because of interface changes. To generate the CRD manifest and RBAC policies updated by operator:
```shell
$ make manifests
```
### Deploying Operator
The operator deployment can be done via `helm` cli, we just need to define the custom image name and tag for testing the operator functionality:
```shell
$ helm upgrade redis-operator ot-helm/redis-operator \
--install --namespace ot-operators --set redisOperator.imageName=<custom-url> \
--set redisOperator.imageTag=<customTag>
```
```shell
# For deploying standalone redis
$ helm upgrade redis ot-helm/redis --namespace ot-operators
# For deploying cluster redis
$ helm upgrade redis-cluster ot-helm/redis-cluster \n
--set redisCluster.clusterSize=3 --install --namespace ot-operators \
--set pdb.enabled=false --set redisCluster.tag=v7.0.5-beta
```
## Docker Image Development
Development of redis docker image is maintained inside a different repository - https://github.com/OT-CONTAINER-KIT/redis. To make any change or suggestion related to Redis docker image, please refer to this repository and make required changes.
In the repository, we have `Dockerfile` for [Redis](https://github.com/OT-CONTAINER-KIT/redis/blob/master/Dockerfile) and [Redis Exporter](https://github.com/OT-CONTAINER-KIT/redis/blob/master/Dockerfile.exporter)
For building the docker image for redis and redis exporter, there are simple make commands:
```shell
$ make build-redis
$ make build-redis-exporter
```

View File

@ -0,0 +1,114 @@
---
title: "Cluster"
linkTitle: "Cluster"
weight: 20
date: 2022-11-02T00:19:19Z
description: >
Instructions for setting up Redis cluster
---
## Architecture
A Redis cluster is simply a [data sharding strategy](https://www.digitalocean.com/community/tutorials/understanding-database-sharding). It automatically partitions data across multiple Redis nodes. It is an advanced feature of Redis which achieves distributed storage and prevents a single point of failure.
In case of any redis node failure, a follower pod will automatically promote as the leader and whenever the old follower node will come back online, it will start acting as a follower. There are a minimum of 3 nodes required to build a Redis-sharded cluster with leader-only architecture. If we include followers as well, there will be at least 6 pods/processes of Redis.
<div align="center" class="mb-0">
<img src="../../../images/cluster-redis.png">
</div>
## Helm Installation
For redis cluster setup we can use `helm` command with the reference of cluster helm chart and additional properties:
```shell
$ helm install redis-cluster ot-helm/redis-cluster \
--set redisCluster.clusterSize=3 --namespace ot-operators
...
Release "redis-cluster" does not exist. Installing it now.
NAME: redis-cluster
LAST DEPLOYED: Sun May 2 16:11:38 2021
NAMESPACE: ot-operators
STATUS: deployed
REVISION: 1
TEST SUITE: None
```
Verify the cluster by checking the pod status of leader and follower pods.
```shell
$ kubectl get pods -n ot-operators
...
NAME READY STATUS RESTARTS AGE
redis-cluster-follower-0 1/1 Running 0 149m
redis-cluster-follower-1 1/1 Running 0 150m
redis-cluster-follower-2 1/1 Running 0 151m
redis-cluster-leader-0 1/1 Running 0 149m
redis-cluster-leader-1 1/1 Running 0 150m
redis-cluster-leader-2 1/1 Running 0 151m
```
If all the pods are in the running state of leader and follower Statefulsets, then we can check the health of the redis cluster by using redis-cli.
```shell
$ kubectl exec -it redis-cluster-leader-0 -n ot-operators -- redis-cli -a Opstree@1234 cluster nodes
...
Defaulting container name to redis-leader.
Use 'kubectl describe pod/redis-leader-0 -n ot-operators' to see all of the containers in this pod.
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
528438a759cee4528c3071d17d75b27b0818555d 10.42.0.219:6379@16379 myself,master - 0 1619952294000 1 connected 0-5460
8ec7812903b7e046bec2f2a7bce4a9ccadfa4188 10.42.0.221:6379@16379 slave d0ff3892d2eba0b2707199cb5df57adbba214bcd 0 1619952297241 3 connected
60f932272322bafbd8c3e16328d26af676aeb8d6 10.42.0.220:6379@16379 slave 6e80da4902802ebffa94cbac9b7d98e9fd74121f 0 1619952297000 2 connected
6e80da4902802ebffa94cbac9b7d98e9fd74121f 10.42.2.178:6379@16379 master - 0 1619952297000 2 connected 5461-10922
d0ff3892d2eba0b2707199cb5df57adbba214bcd 10.42.1.178:6379@16379 master - 0 1619952298245 3 connected 10923-16383
c2b74bd2a360068db01dfc8f00b8d0b012e21215 10.42.1.177:6379@16379 slave 528438a759cee4528c3071d17d75b27b0818555d 0 1619952297000 1 connected
```
## YAML Installation
[Examples](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example) folder has different types of manifests for different scenarios and features. There are these YAML examples present in this directory:
- [additional_config](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/additional_config)
- [advance_config](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/advance_config)
- [affinity](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/affinity)
- [disruption_budget](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/disruption_budget)
- [external_service](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/external_service)
- [password_protected](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/password_protected)
- [private_registry](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/private_registry)
- [probes](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/probes)
- [redis_monitoring](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/redis_monitoring)
- [tls_enabled](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/tls_enabled)
- [upgrade_strategy](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/upgrade-strategy)
A sample manifest for deploying redis cluster:
```yaml
---
apiVersion: redis.redis.opstreelabs.in/v1beta1
kind: RedisCluster
metadata:
name: redis-cluster
spec:
clusterSize: 3
clusterVersion: v7
securityContext:
runAsUser: 1000
fsGroup: 1000
persistenceEnabled: true
kubernetesConfig:
image: quay.io/opstree/redis:v7.0.5
imagePullPolicy: Always
storage:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
```
The yaml manifest can easily get applied by using `kubectl`.
```shell
$ kubectl apply -f cluster.yaml
```

View File

@ -0,0 +1,59 @@
---
title: "Failover Testing"
linkTitle: "Failover Testing"
weight: 30
date: 2022-11-02T00:19:19Z
description: >
Instructions for testing the failover of Redis cluster
---
For cluster setup, testing can be performed to validate the failover functionality of Redis. In the failover testing, we can set some random keys inside the redis cluster and then delete one or two pods from the redis cluster. At that particular time, we can make some calls to redis for fetching the key to observing its failover mechanism of it.
Before failover testing, we have to write some dummy data inside the Redis cluster, we can write the dummy data using the `redis-cli`.
```shell
$ kubectl exec -it redis-cluster-leader-0 -n ot-operators \
-- redis-cli -c set tony stark
...
OK
```
Verify the key has been inserted properly inside the redis by fetching its value. Again we will use `redis-cli` for fetching the key from redis.
```shell
$ kubectl exec -it redis-cluster-leader-0 -n ot-operators \
-- redis-cli -c get tony
...
"stark"
```
To validate the failover functionality, we need to delete few of the pods from the redis cluster. `kubectl` cli could be use for deleting pods from the cluster.
```shell
$ kubectl delete pod redis-cluster-leader-0 -n ot-operators
...
pod "redis-cluster-leader-0" deleted
```
Since we have restarted `redis-cluster-leader-0` pod, we will again list out the redis nodes using `redis-cli` to see if follower node attached to it is promoted as leader or not. Also, the leader role should have been changed to follower role.
```shell
$ kubectl exec -it redis-cluster-leader-0 -n ot-operators \
-- redis-cli cluster nodes
...
eef84b7dada737051c32d592bd66652b9af0cb35 10.42.2.184:6379@16379 slave 0a36dc5064b0a61afa8bd850e93ff0a1c2267704 0 1619958171517 3 connected
a7c424b5ec0e696aa7be15a691846c8820e48cd1 10.42.1.181:6379@16379 master - 0 1619958172520 4 connected 0-5460
118dbe4f49fa224b7d48fbe71990d054c7e9e853 10.42.0.228:6379@16379 slave 85747fe5cabf96e00fd0365737996a93e05cf947 0 1619958173523 2 connected
50c3f58a1e2911a68b614f6a1a766cc4a7063e95 10.42.0.229:6379@16379 myself,slave a7c424b5ec0e696aa7be15a691846c8820e48cd1 0 1619958172000 4 connected
0a36dc5064b0a61afa8bd850e93ff0a1c2267704 10.42.1.183:6379@16379 master - 0 1619958173000 3 connected 10923-16383
85747fe5cabf96e00fd0365737996a93e05cf947 10.42.2.182:6379@16379 master - 0 1619958173523 2 connected 5461-10922
```
So if you notice the output of cluster nodes command, the node IP is updated, and its connected as a leader.
```shell
$ kubectl exec -it redis-cluster-follower-1 -n ot-operators \
-- redis-cli -c get tony
...
"stark"
```

View File

@ -0,0 +1,108 @@
---
title: "Replication"
linkTitle: "Replication"
weight: 10
date: 2023-04-05T19:00:00Z
description: >
Instructions for setting up Redis Replication
---
## Architecture
Redis is an in-memory key-value store that can be used as a database, cache, and message broker. Redis replication is the process of synchronizing data from a Redis leader node to one or more Redis follower nodes.
In Redis replication, the leader node is responsible for receiving write requests and propagating the changes to one or more follower nodes. The follower nodes receive the data changes from the leader and apply them locally, thereby creating a replica of the leader's dataset.
Redis replication uses asynchronous replication, which means that the leader node does not wait for the follower nodes to apply the changes before sending new updates. Instead, the follower nodes catch up with the leader node as soon as they can, based on the available network bandwidth and the capacity of the hardware.
Redis replication is a powerful feature that enhances the durability and scalability of Redis applications. By using Redis replication, you can distribute the workload across multiple nodes, improve the read performance, and ensure data availability in case of a node failure.
<div align="center" class="mb-0">
<img src="../../../images/replication-redis.png">
</div>
> **Note:** By using Redis Sentinel, you can ensure that your Redis Replication remains available even if one or more nodes go down, improving the resilience and reliability of your application.
## Helm Installation
For redis replication setup we can use `helm` command with the reference of replication helm chart and additional properties:
```shell
$ helm install redis-replication ot-helm/redis-replication \
--set redisreplication.clusterSize=3 --namespace ot-operators
...
NAME: redis-replication
LAST DEPLOYED: Tue Mar 21 22:47:44 2023
NAMESPACE: ot-operators
STATUS: deployed
REVISION: 1
TEST SUITE: None
```
Verify the replication-cluster by checking the pod status of pods.
```shell
$ kubectl get pods -n ot-operators
...
NAME READY STATUS RESTARTS AGE
redis-replication-0 1/1 Running 0 2m23s
redis-replication-1 1/1 Running 0 99s
redis-replication-2 1/1 Running 0 59s
```
If all the pods are in the running, then we can check the health of the redis replication-cluster by using redis-cli. Here by default the 0th index redis pod is promoted to master.
## YAML Installation
[Examples](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example) folder has different types of manifests for different scenarios and features. There are these YAML examples present in this directory:
- [additional_config](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/additional_config)
- [advance_config](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/advance_config)
- [affinity](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/affinity)
- [disruption_budget](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/disruption_budget)
- [external_service](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/external_service)
- [password_protected](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/password_protected)
- [private_registry](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/private_registry)
- [probes](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/probes)
- [redis_monitoring](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/redis_monitoring)
- [tls_enabled](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/tls_enabled)
- [upgrade_strategy](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/upgrade-strategy)
A sample manifest for deploying redis replication-cluster:
```yaml
---
apiVersion: redis.redis.opstreelabs.in/v1beta1
kind: RedisReplication
metadata:
name: redis-replication
spec:
clusterSize: 3
securityContext:
runAsUser: 1000
fsGroup: 1000
kubernetesConfig:
image: quay.io/opstree/redis:v7.0.5
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 101m
memory: 128Mi
limits:
cpu: 101m
memory: 128Mi
storage:
volumeClaimTemplate:
spec:
# storageClassName: standard
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
```
The yaml manifest can easily get applied by using `kubectl`.
```shell
kubectl apply -f replication.yaml
```

View File

@ -0,0 +1,95 @@
---
title: "Sentinel"
linkTitle: "sentinel"
weight: 20
date: 2023-04-05T19:00:00Z
description: >
Instructions for setting up Redis sentinel
---
## Architecture
Redis Sentinel is a tool that provides automatic failover and monitoring for Redis nodes. It works by running separate processes that communicate with each other and with Redis nodes to detect failures, elect a new master node, and configure the other nodes to replicate from the new master. Sentinel can also perform additional tasks such as sending notifications and managing configuration changes. Redis Sentinel is a flexible and robust solution for implementing high availability in Redis.
<div align="center" class="mb-0">
<img src="../../../images/sentinel-redis.png">
</div>
## Helm Installation
In redis sentinel mode, we deploy redis as a single StatefulSet pod that means ease of setup but no complexity, no high availability, and no resilience.
Installation can be easily done via `helm` command:
```shell
$ helm install redis-sentinel ot-helm/redis-sentinel \
--set redissentinel.clusterSize=3 --namespace ot-operators \
--set redisSentinelConfig.redisReplicationName="redis-replication"
...
NAME: redis-sentinel
LAST DEPLOYED: Tue Mar 21 23:11:57 2023
NAMESPACE: ot-operators
STATUS: deployed
REVISION: 1
TEST SUITE: None
```
Verify the sentinel redis setup by kubectl command line.
```shell
$ kubectl get pods -n ot-operators
...
NAME READY STATUS RESTARTS AGE
redis-sentinel-0 1/1 Running 0 3m40s
redis-sentinel-1 1/1 Running 0 2m55s
redis-sentinel-2 1/1 Running 0 2m10s
```
## YAML Installation
[Examples](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example) folder has different types of manifests for different scenarios and features. There are these YAML examples present in this directory:
- [additional_config](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/additional_config)
- [advance_config](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/advance_config)
- [affinity](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/affinity)
- [disruption_budget](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/disruption_budget)
- [external_service](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/external_service)
- [password_protected](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/password_protected)
- [private_registry](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/private_registry)
- [probes](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/probes)
- [redis_monitoring](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/redis_monitoring)
- [tls_enabled](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/tls_enabled)
- [upgrade_strategy](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/upgrade-strategy)
A basic sample manifest for sentinel redis:
```yaml
---
apiVersion: redis.redis.opstreelabs.in/v1beta1
kind: RedisSentinel
metadata:
name: redis-sentinel
spec:
clusterSize: 3
securityContext:
runAsUser: 1000
fsGroup: 1000
redisSentinelConfig:
redisReplicationName : redis-replication
kubernetesConfig:
image: quay.io/opstree/redis-sentinel:v7.0.7
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 101m
memory: 128Mi
limits:
cpu: 101m
memory: 128Mi
```
The yaml manifest can easily get applied by using `kubectl`.
```shell
$ kubectl apply -f sentinel.yaml
```

View File

@ -0,0 +1,90 @@
---
title: "Standalone"
linkTitle: "Standalone"
weight: 10
date: 2022-11-02T00:19:19Z
description: >
Instructions for setting up Redis standalone
---
## Architecture
Redis standalone is a single process-based redis pod that can manage your keys inside it. Multiple applications can consume this redis with a Kubernetes endpoint or service. Since this standalone setup is running inside Kubernetes, the auto-heal feature will be automatically part of it. The only drawback of a standalone setup is that it doesn't stand on the high availability principle.
<div align="center" class="mb-0">
<img src="../../../images/standalone-redis.png">
</div>
## Helm Installation
In redis standalone mode, we deploy redis as a single StatefulSet pod that means ease of setup but no complexity, no high availability, and no resilience.
Installation can be easily done via `helm` command:
```shell
$ helm install redis ot-helm/redis --namespace ot-operators
...
Release "redis" does not exist. Installing it now.
NAME: redis
LAST DEPLOYED: Sun May 2 15:59:48 2021
NAMESPACE: ot-operators
STATUS: deployed
REVISION: 1
TEST SUITE: None
```
Verify the standalone redis setup by kubectl command line.
```shell
$ kubectl get pods -n ot-operators
...
NAME READY STATUS RESTARTS AGE
redis-standalone-0 2/2 Running 0 56s
```
## YAML Installation
[Examples](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example) folder has different types of manifests for different scenarios and features. There are these YAML examples present in this directory:
- [additional_config](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/additional_config)
- [advance_config](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/advance_config)
- [affinity](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/affinity)
- [disruption_budget](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/disruption_budget)
- [external_service](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/external_service)
- [password_protected](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/password_protected)
- [private_registry](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/private_registry)
- [probes](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/probes)
- [redis_monitoring](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/redis_monitoring)
- [tls_enabled](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/tls_enabled)
- [upgrade_strategy](https://github.com/OT-CONTAINER-KIT/redis-operator/tree/master/example/upgrade-strategy)
A basic sample manifest for standalone redis:
```yaml
---
apiVersion: redis.redis.opstreelabs.in/v1beta1
kind: Redis
metadata:
name: redis-standalone
spec:
kubernetesConfig:
image: quay.io/opstree/redis:v7.0.5
imagePullPolicy: IfNotPresent
storage:
volumeClaimTemplate:
spec:
# storageClassName: standard
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
securityContext:
runAsUser: 1000
fsGroup: 1000
```
The yaml manifest can easily get applied by using `kubectl`.
```shell
$ kubectl apply -f standalone.yaml
```

View File

@ -0,0 +1,15 @@
---
title: "Getting Started"
linkTitle: "Getting Started"
weight: 4
date: 2022-11-02T00:19:19Z
description: >
Instructions for getting started on Redis and Redis cluster setup
---
The redis operator supports below deployment strategies for redis:-
- Cluster setup
- Standalone setup
- Replication setup
- Sentinel setup

View File

@ -0,0 +1,108 @@
---
title: "Create Cluster"
linkTitle: "Create Cluster"
weight: 10
date: 2022-11-02T00:19:19Z
description: >
Instructions for creating a Kubernetes cluster and installing Redis Operator on it
---
Redis Operator needs a Kubernetes or Openshift cluster for provisioning a Redis setup. This guide helps in setting up a Kubernetes cluster from a quickstart perspective.
Tools involved in this kind of setup:
- [Eksctl](https://eksctl.io/)
- [Minikube](https://minikube.sigs.k8s.io/docs/start/)
## Amazon EKS Cluster
To create a Kubernetes cluster on AWS, we need to download and install the [eksctl](https://eksctl.io/) on the local system and then [eks-cluster.yaml](https://github.com/OT-CONTAINER-KIT/redis-operator/blob/master/example/eks-cluster.yaml) can be executed with it for cluster creation.
The content of [eks-cluster.yaml](https://github.com/OT-CONTAINER-KIT/redis-operator/blob/master/example/eks-cluster.yaml) looks like:
```yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: operator-testing
region: us-west-2
version: "1.22"
managedNodeGroups:
- name: ng-1
instanceType: t3a.medium
desiredCapacity: 3
volumeSize: 30
ssh:
allow: true
volumeType: gp3
kubernetesNetworkConfig:
ipFamily: IPv4
# ipFamily: IPv6
addons:
- name: vpc-cni
- name: coredns
- name: kube-proxy
iam:
withOIDC: true
```
```shell
$ eksctl create cluster -f example/eks-cluster.yaml
...
2022-11-01 12:49:15 [] eksctl version 0.114.0
2022-11-01 12:49:15 [] using region us-west-2
2022-11-01 12:49:16 [] setting availability zones to [us-west-2b us-west-2a us-west-2d]
2022-11-01 12:49:16 [] subnets for us-west-2b - public:192.168.0.0/19 private:192.168.96.0/19
2022-11-01 12:49:16 [] subnets for us-west-2a - public:192.168.32.0/19 private:192.168.128.0/19
2022-11-01 12:49:16 [] subnets for us-west-2d - public:192.168.64.0/19 private:192.168.160.0/19
2022-11-01 12:49:16 [] nodegroup "ng-1" will use "" [AmazonLinux2/1.22]
2022-11-01 12:49:16 [] using SSH public key "/Users/abhishekdubey/.ssh/id_rsa.pub" as "eksctl-operator-testing-nodegroup-ng-1-8b:2b:b2:fc:4c:7f:9c:0d:54:14:70:39:25:b5:6d:60"
2022-11-01 12:49:18 [] using Kubernetes version 1.22
2022-11-01 12:49:18 [] creating EKS cluster "operator-testing" in "us-west-2" region with managed nodes
2022-11-01 12:49:18 [] 1 nodegroup (ng-1) was included (based on the include/exclude rules)
2022-11-01 12:49:18 [] will create a CloudFormation stack for cluster itself and 0 nodegroup stack(s)
2022-11-01 12:49:18 [] will create a CloudFormation stack for cluster itself and 1 managed nodegroup stack(s)
2022-11-01 12:49:18 [] if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=us-west-2 --cluster=operator-testing'
2022-11-01 12:49:18 [] Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "operator-testing" in "us-west-2"
2022-11-01 12:49:18 [] CloudWatch logging will not be enabled for cluster "operator-testing" in "us-west-2"
2022-11-01 12:49:18 [] you can enable it with 'eksctl utils update-cluster-logging --enable-types={SPECIFY-YOUR-LOG-TYPES-HERE (e.g. all)} --region=us-west-2 --cluster=operator-testing'
2022-11-01 13:08:05 [] waiting for CloudFormation stack "eksctl-operator-testing-nodegroup-ng-1"
2022-11-01 13:08:05 [] waiting for the control plane to become ready
2022-11-01 13:08:06 [✔] saved kubeconfig as "/Users/abhishekdubey/.kube/lab-config"
2022-11-01 13:08:06 [] no tasks
2022-11-01 13:08:06 [✔] all EKS cluster resources for "operator-testing" have been created
2022-11-01 13:08:08 [] nodegroup "ng-1" has 3 node(s)
2022-11-01 13:08:08 [] node "ip-192-168-25-130.us-west-2.compute.internal" is ready
2022-11-01 13:08:08 [] node "ip-192-168-38-199.us-west-2.compute.internal" is ready
2022-11-01 13:08:08 [] node "ip-192-168-89-35.us-west-2.compute.internal" is ready
2022-11-01 13:08:08 [] waiting for at least 3 node(s) to become ready in "ng-1"
2022-11-01 13:08:08 [] nodegroup "ng-1" has 3 node(s)
2022-11-01 13:08:08 [] node "ip-192-168-25-130.us-west-2.compute.internal" is ready
2022-11-01 13:08:08 [] node "ip-192-168-38-199.us-west-2.compute.internal" is ready
2022-11-01 13:08:08 [] node "ip-192-168-89-35.us-west-2.compute.internal" is ready
2022-11-01 13:08:11 [] no recommended policies found, proceeding without any IAM
```
## Minikube
Minikube is a tool for creation of Kubernetes on local system for Development purpose. It requires a [Docker](https://docker.com) compatible system or virtual machine environment.
```shell
$ minikube start --vm-driver virtualbox
...
😄 minikube v1.0.1 on linux (amd64)
🤹 Downloading Kubernetes v1.14.1 images in the background ...
🔥 Creating kvm2 VM (CPUs=2, Memory=2048MB, Disk=20000MB) ...
📶 "minikube" IP address is 192.168.39.240
🐳 Configuring Docker as the container runtime ...
🐳 Version of container runtime is 18.06.3-ce
⌛ Waiting for image downloads to complete ...
✨ Preparing Kubernetes environment ...
🚜 Pulling images required by Kubernetes v1.14.1 ...
🚀 Launching Kubernetes v1.14.1 using kubeadm ...
⌛ Waiting for pods: apiserver proxy etcd scheduler controller dns
🔑 Configuring cluster permissions ...
🤔 Verifying component health .....
💗 kubectl is now configured to use "minikube"
🏄 Done! Thank you for using minikube!
```

View File

@ -0,0 +1,58 @@
---
title: "Installation"
linkTitle: "Installation"
weight: 10
date: 2022-11-02T00:19:19Z
description: >
Instructions for installation of Redis Operator.
---
Redis Operator is developed as CRD(Custom Resource Definition) to deploy and manage Redis in standalone/cluster mode. So CRD is an amazing feature of Kubernetes which allows us to create our own resources and APIs in Kubernetes. For further information about CRD, please go through the [official documentation](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/).
There are four different Objects available under `redis.redis.opstreelabs.in/v1beta1`:
- Redis
- Redis Cluster
- Redis Replication
- Redis Sentinel
For [OperatorHub](https://operatorhub.io) installation:
https://operatorhub.io/operator/redis-operator
So for deploying the redis-operator and setup we need a Kubernetes cluster 1.18+ and thats it. Lets deploy the redis operator first.
The easiest way to install a redis operator is using Helm chart. The operator helm chart is developed on the `helm=>3.0.0` version. The [values.yaml](https://github.com/OT-CONTAINER-KIT/helm-charts/blob/main/charts/redis-operator/values.yaml) can be modified.
## Helm Installation
```shell
$ helm repo add ot-helm https://ot-container-kit.github.io/helm-charts/
$ helm install redis-operator ot-helm/redis-operator --namespace ot-operators
...
Release "redis-operator" does not exist. Installing it now.
NAME: redis-operator
LAST DEPLOYED: Sun May 2 14:42:23 2021
NAMESPACE: ot-operators
STATUS: deployed
REVISION: 1
TEST SUITE: None
```
## YAML Installation
{{< alert title="Warning" color="warning">}}
YAML installation is not a recommended way for installation, this can only be used for development practices only.
{{< /alert >}}
```shell
$ bash install-operator.sh
...
customresourcedefinition.apiextensions.k8s.io/redis.redis.redis.opstreelabs.in created
customresourcedefinition.apiextensions.k8s.io/redisclusters.redis.redis.opstreelabs.in created
namespace/ot-operators created
deployment.apps/redis-operator created
serviceaccount/redis-operator created
clusterrole.rbac.authorization.k8s.io/redis-operator created
clusterrolebinding.rbac.authorization.k8s.io/redis-operator created
```

View File

@ -0,0 +1,82 @@
---
title: "Upgrade"
linkTitle: "Upgrade"
weight: 10
date: 2022-11-02T00:19:19Z
description: >
Instructions for upgrading Redis Operator
---
{{< alert color="info" title="Note" >}}
Whichever approach you take to upgrading Redis Operator, make sure to test it in your development environment
before applying it to production.
{{< /alert >}}
## Upgrading Operator
The following are strategies for safely upgrading Redis Operator from one version to another. They may require adjustment to your particular game architecture but should provide a solid foundation for updating Agones safely.
Ideally we should disable the reconcillation on all the Redis setup managed by operator. To disable the reconcillation, we need to add an annotation on all the `Redis` and `Redis Cluster` object.
For `Redis` standalone object:
```yaml
annotations:
redis.opstreelabs.in/skip-reconcile: "true"
```
For `RedisCluster` object:
```yaml
annotations:
rediscluster.opstreelabs.in/skip-reconcile: "true"
```
For `RedisReplication` object:
```yaml
annotations:
redisReplication.opstreelabs.in/skip-reconcile: "true"
```
For `RedisSentinel` object:
```yaml
annotations:
redisSentinel.opstreelabs.in/skip-reconcile: "true"
```
### Upgrading with Helm
Helm features capabilities for upgrading to newer versions of Agones without having to uninstall Redis Operator completely.
For details on how to use Helm for upgrades, see the [helm upgrade](https://v2.helm.sh/docs/helm/#helm-upgrade) documentation.
```shell
$ helm install redis-operator ot-helm/redis-operator \
--namespace ot-operators --version <desired_version>
```
Once upgrading activity is completed, again validate the setup by steps defined in [Validation](../validation).
### Upgrading with YAML
If you installed Redis Operator with [install-operator.sh](https://github.com/OT-CONTAINER-KIT/redis-operator/blob/master/install-operator.sh), we need to update the image tag version inside the [deployment manifest](https://github.com/OT-CONTAINER-KIT/redis-operator/blob/master/config/manager/manager.yaml) of operator and again run the same script.
```yaml
spec:
securityContext:
runAsNonRoot: true
containers:
- command:
- /manager
args:
- --leader-elect
- -zap-log-level=info
image: quay.io/opstree/redis-operator:<desired_version>
imagePullPolicy: Always
```
```shell
$ bash install-operator.sh
```

View File

@ -0,0 +1,40 @@
---
title: "Installation Validation"
linkTitle: "Installation Validation"
weight: 10
date: 2022-11-02T00:19:19Z
description: >
Instructions for validating installation of Operator
---
To confirm Redis Operator is up and running, run the following command:
```shell
$ kubectl describe --namespace ot-operators pods
```
It should describe one pod created in the ot-operators namespace, with no error messages or status. All Conditions sections should look like this:
```yaml
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
```
The operator pod should be in a RUNNING state:
```shell
$ kubectl get pods -n ot-operators
...
NAME READY STATUS RESTARTS AGE
redis-operator-74b6cbf5c5-td8t7 1/1 Running 0 2m11s
```
Thats it!
Now with Redis Operator installed, you can utilise its [Custom Resource Definitions](https://kubernetes.io/docs/concepts/api-extension/custom-resources/) to create resources of type Redis, RedisCluster and more!

View File

@ -0,0 +1,25 @@
---
title: "Installation"
linkTitle: "Installation"
weight: 3
date: 2022-11-02T00:19:19Z
description: >
Instructions for installation of Kubernetes cluster and Redis Operator
---
## Usage Requirements
- **Kubernetes cluster version > 1.18**
- All Kubernetes platform are supported like: [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/), [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/), [Amazon EKS](https://aws.amazon.com/eks/) and [Minikube](https://github.com/kubernetes/minikube).
- Redis cluster needs persistence support in Kubernetes cluster
## Supported Container Architectures
The following container operating systems and architectures can be utilised with Redis Operaror:
| OS | Architecture | Support |
|-------|--------------|----------|
| linux | amd64 | stable |
| linux | arm64 | stable |

View File

@ -0,0 +1,109 @@
---
title: "Monitoring"
linkTitle: "Monitoring"
weight: 8
date: 2022-11-02T00:19:19Z
description: >
Monitoring of Redis standalone and cluster setup using Prometheus
---
The redis-operator uses [redis-exporter](https://github.com/oliver006/redis_exporter) to expose metrics of redis setup in Prometheus format. This exporter captures metrics for both redis standalone and cluster setup.
The monitoring architecture is illustrated in the diagram:
![redis_operator_architecture](../../../images/redis-operator-architecture.png)
For the helm chart installation of redis setup, we can simply enable the redis exporter by creating a custom values file for helm chart. The content of the values file will look like this:
```yaml
redisExporter:
enabled: true
image: quay.io/opstree/redis-exporter:1.0
imagePullPolicy: Always
```
When we have defined the redis-exporter related config in values file, we can apply or upgrade the redis setup. We need to pass the created file as an argument to the `helm` command.
Enabling monitoring for standalone setup:
```shell
$ helm upgrade redis ot-helm/redis -f monitoring-values.yaml \
--install --namespace ot-operators
```
Enabling monitoring for cluster setup:
```shell
$ helm upgrade redis-cluster ot-helm/redis-cluster -f monitoring-values.yaml \
--set redisCluster.clusterSize=3 --install --namespace ot-operators
```
## ServiceMonitor
Once the exporter is configured, we may have to update Prometheus to monitor this endpoint. For [Prometheus Operator](https://github.com/prometheus-operator/prometheus-operator), we have to create a CRD based object called ServiceMonitor. We can apply the CRD definition as well using the helm command.
```yaml
serviceMonitor:
enabled: false
interval: 30s
scrapeTimeout: 10s
namespace: monitoring
```
For kubectl related configuration, we may have to create `ServiceMonitor` definition in a YAML manifest and apply it using `kubectl` command.
ServiceMonitor for Redis cluster setup:
```yaml
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: redis-cluster
labels:
redis-operator: "true"
env: production
spec:
selector:
matchLabels:
redis_setup_type: cluster
endpoints:
- port: redis-exporter
interval: 30s
scrapeTimeout: 10s
namespaceSelector:
matchNames:
- monitoring
```
ServiceMonitor for Redis standalone setup:
```yaml
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: redis-standalone
labels:
redis-operator: "true"
env: production
spec:
selector:
matchLabels:
redis_setup_type: standalone
endpoints:
- port: redis-exporter
interval: 30s
scrapeTimeout: 10s
namespaceSelector:
matchNames:
- monitoring
```
## Grafana Dashboards
There is detailed dashboard created for Redis cluster monitoring setup. Refer to that dashboard once the metrics are available inside Prometheus setup.
[Redis Operator Cluster Dashboard for Prometheus](https://github.com/OT-CONTAINER-KIT/redis-operator/blob/master/dashboards/redis-operator-cluster.json)
![redis_grafana_dashboard](../../../images/grafana1.3b7d307c.png)

View File

@ -0,0 +1,52 @@
---
title: "Overview"
linkTitle: "Overview"
weight: 1
date: 2022-11-02T00:19:19Z
description: >
Redis Operator is a software to set up and manage Redis on [Kubernetes](https://kubernetes.io).
---
A Golang based redis operator that will make/oversee Redis standalone/cluster/replication/sentinel mode setup on top of the Kubernetes. It can create a redis cluster setup with best practices on Cloud as well as the Bare-metal environment. Also, it provides an in-built monitoring capability using redis-exporter.
Documentation is available here:- https://ot-container-kit.github.io/redis-operator/
The type of Redis setup which is currently supported:-
- Redis Cluster
- Redis Standalone
- Redis Replication
- Redis Sentinel
This operator only supports versions of redis `=>6`.
## Purpose
There are multiple problems that people face while setting up redis setup on Kubernetes, specially cluster type setup. The purpose of creating this opperator is to provide an easy and production ready interface for redis setup that include best-practices, security controls, monitoring, and management.
## Why Redis Operator?
Here the features which are supported by this operator:-
- Redis cluster, replication and standalone mode setup
- Redis cluster and replication failover and recovery
- Inbuilt monitoring with redis exporter
- Password and password-less setup of redis
- TLS support for additional security layer
- Ipv4 and Ipv6 support for redis setup
- Detailed monitoring grafana dashboard
## Architecture
![redis_operator_architecture](../../../images/redis-operator-architecture.png)
## Code of Conduct
Participation in this project comes under the [CONTRIBUTING.md](https://github.com/OT-CONTAINER-KIT/redis-operator/blob/master/CONTRIBUTING.md)
## What's Next
- Installation of Redis Operator
- Setup of Redis standalone, cluster, replication and sentinel mode
- Monitoring of Redis setup
- Configuration and advance cofiguration of Operator

View File

@ -0,0 +1,59 @@
---
title: "Redis Overview"
linkTitle: "Redis Overview"
weight: 2
date: 2022-11-02T00:19:19Z
description: >
[Redis](https://redis.io/) is an in-memory cache and database that can be used for improving system design.
---
Redis is a popular and opensource in-memory database that supports multiple data structures like strings, hashes, lists, and sets. But similar to other tools, we can scale standalone redis to a particular extent and not beyond that. Thats why we have a cluster mode setup in which we can scale Redis nodes horizontally and then distribute data among those nodes.
Use cases of redis:
- Caching
- Database
- Chat, messaging, and queues
- Gaming leaderboards
- Session store
- Rich media streaming
- Geospatial
Sometimes getting data from disks can be time-consuming. In order to increase the performance, we can put the requests those either need to be served first or rapidly in Redis memory and then the Redis service there will keep rest of the data in the main database. So the whole architecture will look like this:
<div align="center">
<img src="../../../images/redis-cache.png">
</div>
## Sharding vs Replication
This operator generally focuses on two different types of setup i.e. standalone and cluster. In cluster mode, it focuses on sharded cluster only as of now.
There are two models of setting up cluster in redis:
- Sharding
- Replication
Replication is also known as mirroring of data. In replication, all the data get copied from the leader node to the follower node. Sharding is also known as partitioning. It splits up the data by the key to multiple nodes.
<div align="center">
<img src="../../../images/replication.png">
</div>
<div align="center">
<img src="../../../images/sharding.png">
</div>
In sharding, the keys are getting distributed across both machine A and B. That is, the machine A will hold the 1, 3 key and machine B will hold 2, 4 key:
<div align="center">
<img src="https://blog2opstree.files.wordpress.com/2019/06/08d40-1ylzieskl-3rvar6kleoziq.png">
</div>
## Redis cluster challenges on Kubernetes
Kubernetes has made the deployment of stateful application quite easy by StatefulSets. By using StatefulSets, we can easily deploy and scale any kind of stateful applications like Kafka, Zookeeper, etc. But in the case of redis, the setup is not straightforward, there are some additional things which needs to be taken care:-
- We have to use the headless service of Redis because its a TCP based service and normal service is HTTP(Layer 7) based Loadbalancer. So in case of headless service, no ClusterIP will be used, and we have to rely on Pod IP.
- Redis doesnt use DNS to form clusters instead of that it uses IP. So we cannot use the internal DNS name of headless service, instead of that, we have to use Pod IP to form a Redis cluster.
- In Kubernetes, Pod IP is dynamic and it can change after the pod restart, so in case of the restart the cluster will be malformed and the restarted pod will act as a lost node.

View File

@ -0,0 +1,207 @@
---
title: "Release History"
linkTitle: "Release History"
weight: 10
date: 2022-11-02T00:19:19Z
description: >
Release versions and their description about Redis Operator
---
### v0.13.0
##### November 10, 2022
**🐞 Bug Fixes**
- Fixed multiple follower logic for redis cluster
**🎉 Features**
- Updated all examples for Redis v7
- Revamped documentation with the latest information
- Added pause option for reconcilations
- Added support for arm64
- Added update strategy for statefulset
- Added logic for updating follower replicas
- Added TLS feature for standalone
### v0.12.0
##### October 12, 2022
**🐞 Bug Fixes**
- PDB (Pod disruption budget) creation issue
- Fixed cluster recovery logic
- Fixed IP check and conversion logic
- Persistence issue fix
**🎉 Features**
- Added pvc, pv clusterrole fix
- Support for defining serviceAccount
- Closing of redis client connection
- Added finalizer for statefulset
- Added Prometheus service annotation
- Added support for Redis 7 with DNS hostname
### v0.11.0
**July 5, 2022**
**🐞 Bug Fixes**
- Fix Redis cluster and Redis CRD
- Fixed TLS authentication between redis cluster
- Fixed RBAC policy for PDB
- Redis exporter exception handled
- External service fix
### v0.10.0
**January 26, 2022**
**🎉 Features**
- Added custom probes capability
- Added sidecar support for redis
- Added option for namespaced operator
- Added finalizers for Kubernetes resources
- Adding PodDisruptionBudget support
- Added TLS cluster support
- Pass through Annotations and Labels to all Child resources
- Adding Rudimentry IPv6 Support
**🐞 Bug Fixes**
- Fix up RedisClusterStatus Spec being incorrect object
- Fixed invalid RBAC kustomization
- Fixed RBAC role for operator
- Fixed service creation for leader and follower
### v0.9.0
**November 13, 2021**
**🎉 Features**
- Added RBAC policies for redis operator with least privileges
**🐞 Bug Fixes**
- Fix and updated documentation dependencies
- Test pointers before dereferencing
- Fix panic error of golang for redis exporter
- Fix resource block nil exception for redis exporter
### v0.8.0
**September 3, 2021**
**🎉 Features**
- Added external configuration capability for follower and leader
- Streamlined examples folder with different examples for standalone and cluster
- Added the capability for affinity for leader and follower
**🐞 Bug Fixes**
- Fix bug for non-defined storage
- Fixed secret nil exception bug
- Fixed bug for making redis exporter optional
### v0.7.0
**August 12, 2021**
**🎉 Features**
- Remove all the vulnerable dependencies from docs(NodeJS)
- Added a new grafana dashboard for better monitoring visualization
- Added environment variable support for redis exporter
- Added Image Pull Secret support for private registeries
**🐞 Bug Fixes**
- Fix bug for non-defined storage
- Fixed secret nil exception bug
- Fixed bug for making redis exporter optional
### v0.6.0
**June 12, 2021**
**🎉 Features**
- Breaked the CRDs into Redis standalone cluster setup
- Optimized code configuration for creating Redis cluster
- Removed string secret type and secret type password is only supported
- Structured and optimized golang based codebase
- Removed divisive terminlogies
**🐞 Bug Fixes**
- Fixed logic for service and statefulset comparison in K8s
- Removed the monitor label to resolve service endpoint issue
### v0.5.0
**May 1, 2021**
**🎉 Features**
- Added support for recovering redis nodes from failover
- Added toleration support for redis statefuls
- Added capability to use existing secret created inside K8s
**🐞Bug Fixes**
- Fixed logic for service and statefulset comparison in K8s
### v0.4.0
**February 6, 2021**
**🎉 Features**
- Add Nodeport support for Kubernetes service
**🐞 Bug Fixes**
- Updated helm chart with latest CRD configuration
- Optimized helm chart
- RBAC issus fixed
### v0.3.0
**Decemeber 30, 2020**
**🎉 Features**
- Upgraded operator-sdk version to v1.0.3
- Added capability to watch multiple namespaces
- Added CI workflow pipeline for golang
**🐞 Bug Fixes**
- Password updation bug https://github.com/OT-CONTAINER-KIT/redis-operator/issues/21
- POD recovery, Can't Sync pods IP to nodes.conf https://github.com/OT-CONTAINER-KIT/redis-operator/issues/20
- Directory creation (permission issue) https://github.com/OT-CONTAINER-KIT/redis-operator/issues/19
### v0.2.0
**July 1, 2020**
**🎉 Features**
- Added documentation site for better management
- Added YAML validation for redis resource
- Added resources in redis exporter
- Structured complete YAML manifests
- Added service type for redis service
- Updated helm chart with better practices
**🐞 Bug Fixes**
- Fixed redis cluster failover bug
### v0.1.0
**February 21, 2020**
**🎉 Features**
- Cluster/Standalone redis setup
- Password and password-less setup
- Node selector and affinity
- SecurityContext
- Priority Class
- Monitoring support
- PVC and resources support

27
content/en/docs/_index.md Executable file
View File

@ -0,0 +1,27 @@
---
title: "Redis Operator"
linkTitle: Documentation
weight: 20
menu:
main:
weight: 20
date: 2022-11-02T00:19:19Z
description: >
Documentation and usage guides on how to deploy redis operator on top of Kubernetes.
---
Release version: {{< release-version >}}
These pages show you how to get up and running as quickly as possible in Agones.
If you are new to Redis Operator, start with [Overview]({{< relref "./Overview/_index.md" >}}) to get familiar with its
features and services.
The [Installation]({{< relref "./Installation/_index.md" >}}) guide will take you through creating a Kubernetes
cluster and getting Redis Operator installed.

5
content/en/search.md Normal file
View File

@ -0,0 +1,5 @@
---
title: Search Results
layout: search
---

2
credits Normal file
View File

@ -0,0 +1,2 @@
Go Vanity URLs, original code: https://github.com/GoogleCloudPlatform/govanityurls
Background image: https://www.google.com.au/about/datacenters/gallery/index.html#/all/68

90
gen-api-docs.sh Executable file
View File

@ -0,0 +1,90 @@
#!/bin/bash
# Copyright 2019 Google LLC All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -ex
#Reinstall Go 1.15 since the version upon it won't work with gen-crd-api-reference-docs
rm -rf /usr/local/go
GO_VERSION=1.15.13
cd /usr/local
wget -q https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz && \
tar -xzf go${GO_VERSION}.linux-amd64.tar.gz && rm go${GO_VERSION}.linux-amd64.tar.gz
export GOPROXY=http://proxy.golang.org
echo "using go proxy as a workaround for git.agache.org being down: $GOPROXY"
cd /go/src/github.com/ahmetb/gen-crd-api-reference-docs
#Use local version of agones
go mod edit --replace=agones.dev/agones@latest=../../../agones.dev/agones/
go build
cp /go/src/agones.dev/agones/site/assets/templates/pkg.tpl ./template
FILE=${FILE:-/go/src/agones.dev/agones/site/content/en/docs/Reference/agones_crd_api_reference.html}
VERSION=${VERSION:-"0.9.0"}
# +++ Title section
TITLE="/tmp/title.html"
# Current ExpiryVersion
EXPIRY_DOC="/tmp/expiry.html"
# Previous Publish Version after release
PUBLISH_DOC="/tmp/publish.html"
# Output of the gen-crd-api-reference-docs
RESULT="/tmp/agones_crd_api_reference.html"
# Version to compare
OLD="/tmp/old_docs.html"
./gen-crd-api-reference-docs --config ../../../agones.dev/agones/site/assets/templates/crd-doc-config.json --api-dir ../../../agones.dev/agones/pkg/apis/ --out-file $RESULT
awk '/\ feature\ publishVersion/{flag=1;next}/\ \/feature/{flag=0}flag' $FILE > $OLD
# Get the title lines from +++ till empty string
awk '/\+\+\+/ {ok=1} /^$/ {ok=0} {if(ok){print $0}}' $FILE > $TITLE
printf "\n" >> $TITLE
doc_version=$(grep 'feature publishVersion=' $FILE )
echo $doc_version
publish='{{% feature publishVersion="'$VERSION'" %}}'
expiry='{{% feature expiryVersion="'$VERSION'" %}}'
#Get previous expiry version tag
sed '/\ expiryVersion="'$VERSION'"/,/%\ \/feature/!d;/%\ \/feature/q' $FILE > $EXPIRY_DOC
sed '/\ publishVersion=/,/%\ \/feature/!d;/%\ \/feature/q' $FILE > $PUBLISH_DOC
function sedeasy {
sed -i "s/$(echo $1 | sed -e 's/\([[\/.*]\|\]\)/\\&/g')/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3
}
# do we have changes in generated API docs compared to previous version
if ! diff <(sort $RESULT) <(sort $OLD);
then
echo "Output to a file $FILE"
if [ "$publish" != "$doc_version" ]
then
echo "Tagging old publishVersion section with expiryVersion shortcode"
sedeasy "$doc_version" "$expiry" $PUBLISH_DOC
cat $PUBLISH_DOC >> $TITLE
else
echo "expiry version left unchanged"
cat $EXPIRY_DOC >> $TITLE
fi
cat $TITLE > $FILE
echo -e '{{% feature publishVersion="'$VERSION'" %}}' >> $FILE
cat $RESULT >> $FILE
echo -e '{{% /feature %}}\n' >> $FILE
fi

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module github.com/agones/agones/site
go 1.19
require gopkg.in/yaml.v2 v2.2.8

4
go.sum Normal file
View File

@ -0,0 +1,4 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

189
handler.go Normal file
View File

@ -0,0 +1,189 @@
// Copyright 2017 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// govanityurls serves Go vanity URLs.
package main
import (
"errors"
"fmt"
"html/template"
"net/http"
"sort"
"strings"
"gopkg.in/yaml.v2"
)
type handler struct {
host string
cacheControl string
paths pathConfigSet
}
type pathConfig struct {
path string
repo string
display string
vcs string
}
func newHandler(config []byte) (*handler, error) {
var parsed struct {
Host string `yaml:"host,omitempty"`
CacheAge *int64 `yaml:"cache_max_age,omitempty"`
Paths map[string]struct {
Repo string `yaml:"repo,omitempty"`
Display string `yaml:"display,omitempty"`
VCS string `yaml:"vcs,omitempty"`
} `yaml:"paths,omitempty"`
}
if err := yaml.Unmarshal(config, &parsed); err != nil {
return nil, err
}
h := &handler{host: parsed.Host}
cacheAge := int64(86400) // 24 hours (in seconds)
if parsed.CacheAge != nil {
cacheAge = *parsed.CacheAge
if cacheAge < 0 {
return nil, errors.New("cache_max_age is negative")
}
}
h.cacheControl = fmt.Sprintf("public, max-age=%d", cacheAge)
for path, e := range parsed.Paths {
pc := pathConfig{
path: strings.TrimSuffix(path, "/"),
repo: e.Repo,
display: e.Display,
vcs: e.VCS,
}
switch {
case e.Display != "":
// Already filled in.
case strings.HasPrefix(e.Repo, "https://github.com/"):
pc.display = fmt.Sprintf("%v %v/tree/main{/dir} %v/blob/main{/dir}/{file}#L{line}", e.Repo, e.Repo, e.Repo)
case strings.HasPrefix(e.Repo, "https://bitbucket.org"):
pc.display = fmt.Sprintf("%v %v/src/default{/dir} %v/src/default{/dir}/{file}#{file}-{line}", e.Repo, e.Repo, e.Repo)
}
switch {
case e.VCS != "":
// Already filled in.
if e.VCS != "bzr" && e.VCS != "git" && e.VCS != "hg" && e.VCS != "svn" {
return nil, fmt.Errorf("configuration for %v: unknown VCS %s", path, e.VCS)
}
case strings.HasPrefix(e.Repo, "https://github.com/"):
pc.vcs = "git"
default:
return nil, fmt.Errorf("configuration for %v: cannot infer VCS from %s", path, e.Repo)
}
h.paths = append(h.paths, pc)
}
sort.Sort(h.paths)
return h, nil
}
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
current := r.URL.Path
pc, subpath := h.paths.find(current)
if pc == nil && current == "/" {
h.serveIndex(w, r)
return
}
if pc == nil && strings.Contains(current, "/chart/stable") {
path := strings.Replace(current, "/chart/stable", "", 1)
h.serveChart(w, r, path)
return
}
if pc == nil {
http.NotFound(w, r)
return
}
w.Header().Set("Cache-Control", h.cacheControl)
if err := vanityTmpl.Execute(w, struct {
Import string
Subpath string
Repo string
Display string
VCS string
}{
Import: h.Host(r) + pc.path,
Subpath: subpath,
Repo: pc.repo,
Display: pc.display,
VCS: pc.vcs,
}); err != nil {
http.Error(w, "cannot render the page", http.StatusInternalServerError)
}
}
func (h *handler) serveIndex(w http.ResponseWriter, r *http.Request) {
// Just redirect to the first one
// just commenting out, in case we want to soft launch
// http.Redirect(w, r, h.paths[0].repo, http.StatusTemporaryRedirect)
http.Redirect(w, r, "/site/", http.StatusTemporaryRedirect)
}
func (h *handler) serveChart(w http.ResponseWriter, r *http.Request, path string) {
root := "https://storage.googleapis.com/agones-chart"
http.Redirect(w, r, root+path, http.StatusTemporaryRedirect)
}
func (h *handler) Host(r *http.Request) string {
host := h.host
if host == "" {
host = defaultHost(r)
}
return host
}
var vanityTmpl = template.Must(template.New("vanity").Parse(`<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="go-import" content="{{.Import}} {{.VCS}} {{.Repo}}">
<meta name="go-source" content="{{.Import}} {{.Display}}">
<meta http-equiv="refresh" content="0; url=https://godoc.org/{{.Import}}/{{.Subpath}}">
</head>
<body>
Nothing to see here; <a href="https://godoc.org/{{.Import}}/{{.Subpath}}">see the package on godoc</a>.
</body>
</html>`))
type pathConfigSet []pathConfig
func (pset pathConfigSet) Len() int {
return len(pset)
}
func (pset pathConfigSet) Less(i, j int) bool {
return pset[i].path < pset[j].path
}
func (pset pathConfigSet) Swap(i, j int) {
pset[i], pset[j] = pset[j], pset[i]
}
func (pset pathConfigSet) find(path string) (pc *pathConfig, subpath string) {
i := sort.Search(len(pset), func(i int) bool {
return pset[i].path >= path
})
if i < len(pset) && pset[i].path == path {
return &pset[i], ""
}
if i > 0 && strings.HasPrefix(path, pset[i-1].path+"/") {
return &pset[i-1], path[len(pset[i-1].path)+1:]
}
return nil, ""
}

284
handler_test.go Normal file
View File

@ -0,0 +1,284 @@
// Copyright 2017 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"bytes"
"io"
"net/http"
"net/http/httptest"
"sort"
"testing"
)
func TestHandler(t *testing.T) {
tests := []struct {
name string
config string
path string
goImport string
goSource string
}{
{
name: "explicit display",
config: "host: example.com\n" +
"paths:\n" +
" /portmidi:\n" +
" repo: https://github.com/rakyll/portmidi\n" +
" display: https://github.com/rakyll/portmidi _ _\n",
path: "/portmidi",
goImport: "example.com/portmidi git https://github.com/rakyll/portmidi",
goSource: "example.com/portmidi https://github.com/rakyll/portmidi _ _",
},
{
name: "display GitHub inference",
config: "host: example.com\n" +
"paths:\n" +
" /portmidi:\n" +
" repo: https://github.com/rakyll/portmidi\n",
path: "/portmidi",
goImport: "example.com/portmidi git https://github.com/rakyll/portmidi",
goSource: "example.com/portmidi https://github.com/rakyll/portmidi https://github.com/rakyll/portmidi/tree/main{/dir} https://github.com/rakyll/portmidi/blob/main{/dir}/{file}#L{line}",
},
{
name: "Bitbucket Mercurial",
config: "host: example.com\n" +
"paths:\n" +
" /gopdf:\n" +
" repo: https://bitbucket.org/zombiezen/gopdf\n" +
" vcs: hg\n",
path: "/gopdf",
goImport: "example.com/gopdf hg https://bitbucket.org/zombiezen/gopdf",
goSource: "example.com/gopdf https://bitbucket.org/zombiezen/gopdf https://bitbucket.org/zombiezen/gopdf/src/default{/dir} https://bitbucket.org/zombiezen/gopdf/src/default{/dir}/{file}#{file}-{line}",
},
{
name: "Bitbucket Git",
config: "host: example.com\n" +
"paths:\n" +
" /mygit:\n" +
" repo: https://bitbucket.org/zombiezen/mygit\n" +
" vcs: git\n",
path: "/mygit",
goImport: "example.com/mygit git https://bitbucket.org/zombiezen/mygit",
goSource: "example.com/mygit https://bitbucket.org/zombiezen/mygit https://bitbucket.org/zombiezen/mygit/src/default{/dir} https://bitbucket.org/zombiezen/mygit/src/default{/dir}/{file}#{file}-{line}",
},
{
name: "subpath",
config: "host: example.com\n" +
"paths:\n" +
" /portmidi:\n" +
" repo: https://github.com/rakyll/portmidi\n" +
" display: https://github.com/rakyll/portmidi _ _\n",
path: "/portmidi/foo",
goImport: "example.com/portmidi git https://github.com/rakyll/portmidi",
goSource: "example.com/portmidi https://github.com/rakyll/portmidi _ _",
},
{
name: "subpath with trailing config slash",
config: "host: example.com\n" +
"paths:\n" +
" /portmidi/:\n" +
" repo: https://github.com/rakyll/portmidi\n" +
" display: https://github.com/rakyll/portmidi _ _\n",
path: "/portmidi/foo",
goImport: "example.com/portmidi git https://github.com/rakyll/portmidi",
goSource: "example.com/portmidi https://github.com/rakyll/portmidi _ _",
},
}
for _, test := range tests {
h, err := newHandler([]byte(test.config))
if err != nil {
t.Errorf("%s: newHandler: %v", test.name, err)
continue
}
s := httptest.NewServer(h)
resp, err := http.Get(s.URL + test.path)
if err != nil {
s.Close()
t.Errorf("%s: http.Get: %v", test.name, err)
continue
}
data, err := io.ReadAll(resp.Body)
if err != nil {
t.Errorf("Could not read all: %s", err)
continue
}
err = resp.Body.Close()
if err != nil {
t.Errorf("Could not close body: %s", err)
continue
}
s.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("%s: status code = %s; want 200 OK", test.name, resp.Status)
}
if got := findMeta(data, "go-import"); got != test.goImport {
t.Errorf("%s: meta go-import = %q; want %q", test.name, got, test.goImport)
}
if got := findMeta(data, "go-source"); got != test.goSource {
t.Errorf("%s: meta go-source = %q; want %q", test.name, got, test.goSource)
}
}
}
func TestBadConfigs(t *testing.T) {
badConfigs := []string{
"paths:\n" +
" /missingvcs:\n" +
" repo: https://bitbucket.org/zombiezen/gopdf\n",
"paths:\n" +
" /unknownvcs:\n" +
" repo: https://bitbucket.org/zombiezen/gopdf\n" +
" vcs: xyzzy\n",
"cache_max_age: -1\n" +
"paths:\n" +
" /portmidi:\n" +
" repo: https://github.com/rakyll/portmidi\n",
}
for _, config := range badConfigs {
_, err := newHandler([]byte(config))
if err == nil {
t.Errorf("expected config to produce an error, but did not:\n%s", config)
}
}
}
func findMeta(data []byte, name string) string {
var sep []byte
sep = append(sep, `<meta name="`...)
sep = append(sep, name...)
sep = append(sep, `" content="`...)
i := bytes.Index(data, sep)
if i == -1 {
return ""
}
content := data[i+len(sep):]
j := bytes.IndexByte(content, '"')
if j == -1 {
return ""
}
return string(content[:j])
}
func TestPathConfigSetFind(t *testing.T) {
tests := []struct {
paths []string
query string
want string
subpath string
}{
{
paths: []string{"/portmidi"},
query: "/portmidi",
want: "/portmidi",
},
{
paths: []string{"/portmidi"},
query: "/portmidi/",
want: "/portmidi",
},
{
paths: []string{"/portmidi"},
query: "/foo",
want: "",
},
{
paths: []string{"/portmidi"},
query: "/zzz",
want: "",
},
{
paths: []string{"/abc", "/portmidi", "/xyz"},
query: "/portmidi",
want: "/portmidi",
},
{
paths: []string{"/abc", "/portmidi", "/xyz"},
query: "/portmidi/foo",
want: "/portmidi",
subpath: "foo",
},
}
emptyToNil := func(s string) string {
if s == "" {
return "<nil>"
}
return s
}
for _, test := range tests {
pset := make(pathConfigSet, len(test.paths))
for i := range test.paths {
pset[i].path = test.paths[i]
}
sort.Sort(pset)
pc, subpath := pset.find(test.query)
var got string
if pc != nil {
got = pc.path
}
if got != test.want || subpath != test.subpath {
t.Errorf("pathConfigSet(%v).find(%q) = %v, %v; want %v, %v",
test.paths, test.query, emptyToNil(got), subpath, emptyToNil(test.want), test.subpath)
}
}
}
func TestCacheHeader(t *testing.T) {
tests := []struct {
name string
config string
cacheControl string
}{
{
name: "default",
cacheControl: "public, max-age=86400",
},
{
name: "specify time",
config: "cache_max_age: 60\n",
cacheControl: "public, max-age=60",
},
{
name: "zero",
config: "cache_max_age: 0\n",
cacheControl: "public, max-age=0",
},
}
for _, test := range tests {
h, err := newHandler([]byte("paths:\n /portmidi:\n repo: https://github.com/rakyll/portmidi\n" +
test.config))
if err != nil {
t.Errorf("%s: newHandler: %v", test.name, err)
continue
}
s := httptest.NewServer(h)
resp, err := http.Get(s.URL + "/portmidi")
if err != nil {
t.Errorf("%s: http.Get: %v", test.name, err)
continue
}
err = resp.Body.Close()
if err != nil {
t.Errorf("could not close the body: %s", err)
continue
}
got := resp.Header.Get("Cache-Control")
if got != test.cacheControl {
t.Errorf("%s: Cache-Control header = %q; want %q", test.name, got, test.cacheControl)
}
}
}

24
htmltest.yaml Normal file
View File

@ -0,0 +1,24 @@
# Copyright 2019 Google LLC All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
TestFilesConcurrently: false
IgnoreInternalEmptyHash: true
OutputDir: /go/src/agones.dev/agones/site/tmp
OutputLogFile: .htmltest.log
CheckExternal: true
ExternalTimeout: 60
IgnoreURLs:
- http://localhost
HTTPHeaders:
User-Agent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"

View File

@ -0,0 +1,29 @@
{{ $links := .Site.Params.links }}
<section id="community" class="row td-box td-box--2 td-box--gradient td-box--height-auto">
<div class="col-sm-12 col-md-6 p-5">
<h2>Learn and Connect</h2>
<p>Using or want to use {{ .Site.Title }}? Find out more here:
{{ with index $links "user"}}
{{ template "community-links-list" . }}
{{ end }}
</div>
<div class=" col-sm-12 col-md-6 p-5">
<h2>Develop and Contribute</h2>
<p>If you want to get more involved by contributing to {{ .Site.Title }}, join us here:
{{ with index $links "developer"}}
{{ template "community-links-list" . }}
{{ end }}
<p>You can find out how to contribute to these docs in our <a href="{{ "/docs/contribute/" | relURL}}">Contribution Guidelines</a>.
</div>
</section>
{{ define "community-links-list" }}
<ul>
{{ range . }}
<li title="{{ .name }}">
<a target="_blank" href="{{ .url }}"><i class="{{ .icon }}"></i> {{ .name }}:</a> {{ .desc | safeHTML }}
</li>
{{ end }}
</ul>
{{ end }}

View File

@ -0,0 +1,7 @@
<!-- Favicons as generated by http://cthedot.de/icongen -->
<link rel="shortcut icon" href="{{ "favicons/favicon.ico?v=1" | relURL }}" >
<link rel="apple-touch-icon" href="{{ "favicons/favicon.ico?v=1" | relURL }}" sizes="180x180">
<link rel="icon" type="image/png" href="{{ "favicons/favicon.ico?v=1" | relURL }}" sizes="16x16">
<link rel="icon" type="image/png" href="{{ "favicons/favicon.ico?v=1" | relURL }}" sizes="32x32">
<link rel="apple-touch-icon" href="{{ "favicons/favicon.ico?v=1" | relURL }}" sizes="180x180">

View File

@ -0,0 +1,31 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{{ hugo.Generator }}
{{ if eq (getenv "HUGO_ENV") "production" }}
<META NAME="ROBOTS" CONTENT="INDEX, FOLLOW">
{{ else }}
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
{{ end }}
{{ range .AlternativeOutputFormats -}}
<link rel="{{ .Rel }}" type="{{ .MediaType.Type }}" href="{{ .Permalink | safeURL }}">
{{ end -}}
{{ partialCached "favicons.html" . }}
<title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{ . }} | {{ end }}{{ .Site.Title }}{{ end }}</title>
{{- template "_internal/opengraph.html" . -}}
{{- template "_internal/google_news.html" . -}}
{{- template "_internal/schema.html" . -}}
{{- template "_internal/twitter_cards.html" . -}}
{{ if eq (getenv "HUGO_ENV") "production" }}
{{ template "_internal/google_analytics_async.html" . }}
{{ end }}
{{ if .Site.Params.prism_syntax_highlighting }}
<!-- stylesheet for Prism -->
<link rel="stylesheet" href="{{ "/css/prism.css" | relURL }}"/>
{{ end }}
{{ partialCached "head-css.html" . "asdf" }}
<link rel="stylesheet" type="text/css" href={{ "css/asciinema-player.css" | absURL }} />
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
{{ partial "hooks/head-end.html" . }}

View File

@ -0,0 +1 @@
<script src={{ "js/asciinema-player.js" | absURL }}></script>

View File

@ -0,0 +1,31 @@
{{ $cover := .HasShortcode "blocks/cover" }}
<nav class="js-navbar-scroll navbar navbar-expand navbar-light {{ if not $cover }} nav-shadow {{ end }}flex-column flex-md-row td-navbar">
<a id="agones-top" {{ if $cover }} style="display: none;" {{end}} class="navbar-brand" href="{{ .Site.Home.RelPermalink }}">
{{ with resources.Get "icons/logo.svg" }}{{ ( . | minify).Content | safeHTML }} {{ end }}<span class="text-uppercase font-weight-bold">{{ .Site.Title }}</span>
</a>
<div class="td-navbar-nav-scroll ml-md-auto" id="main_navbar">
<ul class="navbar-nav mt-2 mt-lg-0">
{{ $p := . }}
{{ range .Site.Menus.main }}
<li class="nav-item mr-4 mb-2 mb-lg-0">
{{ $active := or ($p.IsMenuCurrent "main" .) ($p.HasMenuCurrent "main" .) }}
{{ with .Page }}
{{ $active = or $active ( $.IsDescendant .) }}
{{ end }}
<a class="nav-link{{if $active }} active{{end}}" href="{{ with .Page }}{{ .RelPermalink }}{{ else }}{{ .URL | relLangURL }}{{ end }}"><span{{if $active }} class="active"{{end}}>{{ .Name }}</span></a>
</li>
{{ end }}
<li class="nav-item mr-4 mb-2 mb-lg-0">
<a class="nav-link" href="{{ .Site.Params.github_repo }}">GitHub</a>
</li>
{{ if (gt (len .Site.Home.Translations) 0) }}
<li class="nav-item dropdown d-none d-lg-block">
{{ partial "navbar-lang-selector.html" . }}
</li>
{{ end }}
</ul>
</div>
<div class="navbar-nav mx-lg-2 d-none d-lg-block">{{ partial "search-input.html" . }}</div>
</nav>

View File

@ -0,0 +1 @@
{{ T "post_last_mod"}} {{ .Lastmod.Format .Site.Params.time_format_default }}{{ with .GitInfo }}: <a data-proofer-ignore href="{{ $.Site.Params.github_repo }}/commit/{{ .Hash }}">{{ .Subject }} ({{ .AbbreviatedHash }})</a>{{end }}

View File

@ -0,0 +1,11 @@
{{ if .Path }}
{{ $gh_repo := ($.Param "github_repo") }}
{{ if $gh_repo }}
<div class="td-page-meta ml-2 pb-1 pt-2 mb-0">
{{ $editURL := printf "%s/edit/main/site/content/%s/%s" $gh_repo ($.Site.Language.Lang) .Path }}
{{ $issuesURL := printf "%s/issues/new?title=%s" $gh_repo (htmlEscape $.Title )}}
<a href="{{ $editURL }}" data-proofer-ignore target="_blank"><i class="fa fa-edit fa-fw"></i> {{ T "post_edit_this" }}</a>
<a href="{{ $issuesURL }}" target="_blank"><i class="fab fa-github fa-fw"></i> {{ T "post_create_issue" }}</a>
</div>
{{ end }}
{{ end }}

View File

@ -0,0 +1,40 @@
{{ $pag := $.Paginator }}
{{ if gt $pag.TotalPages 1 }}
<ul class="pagination">
{{ with $pag.First }}
<li class="page-item">
<a href="{{ .URL }}" class="page-link" aria-label="First"><span aria-hidden="true">&laquo;&laquo;</span></a>
</li>
{{ end }}
<li class="page-item{{ if not $pag.HasPrev }} disabled{{ end }}">
<a {{ if $pag.HasPrev }}href="{{ $pag.Prev.URL }}"{{ end }} class="page-link" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a>
</li>
{{ $ellipsed := false }}
{{ $shouldEllipse := false }}
{{ range $pag.Pagers }}
{{ $right := sub .TotalPages .PageNumber }}
{{ $showNumber := or (le .PageNumber 3) (eq $right 0) }}
{{ $showNumber := or $showNumber (and (gt .PageNumber (sub $pag.PageNumber 2)) (lt .PageNumber (add $pag.PageNumber 2))) }}
{{ if $showNumber }}
{{ $ellipsed = false }}
{{ $shouldEllipse = false }}
{{ else }}
{{ $shouldEllipse = not $ellipsed }}
{{ $ellipsed = true }}
{{ end }}
{{ if $showNumber }}
<li class="page-item{{ if eq . $pag }} active{{ end }}"><a class="page-link" href="{{ .URL }}">{{ .PageNumber }}</a></li>
{{ else if $shouldEllipse }}
<li class="page-item disabled"><span aria-hidden="true">&nbsp;&hellip;&nbsp;</span></li>
{{ end }}
{{ end }}
<li class="page-item{{ if not $pag.HasNext }} disabled{{ end }}">
<a {{ if $pag.HasNext }}href="{{ $pag.Next.URL }}"{{ end }} class="page-link" aria-label="Next"><span aria-hidden="true">&raquo;</span></a>
</li>
{{ with $pag.Last }}
<li class="page-item">
<a href="{{ .URL }}" class="page-link" aria-label="Last"><span aria-hidden="true">&raquo;&raquo;</span></a>
</li>
{{ end }}
</ul>
{{ end }}

View File

@ -0,0 +1,3 @@
{{ with .Site.Params.gcs_engine_id }}
<input id="agones-search" type="search" class="form-control td-search-input" placeholder="&#xf002 {{ T "ui_search" }}" aria-label="{{ T "ui_search" }}" autocomplete="off">
{{ end }}

View File

@ -0,0 +1,5 @@
{{- if eq (getenv "HUGO_ENV") "production" }}
{{- $.Page.Site.Params.aks_minor_supported_k8s }}
{{- else }}
{{- $.Page.Site.Params.dev_aks_minor_supported_k8s }}
{{- end }}

View File

@ -0,0 +1,11 @@
{{- $gate := split (.Get "gate") "," }}
{{- $len_gate := len $gate }}
{{- $title := .Get "title" }}
<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Warning</h4>
<p>The {{ $title }} {{- if gt $len_gate 1 }} features are{{ else }} feature is{{ end }} currently <strong><a href="{{ ref . "/docs/Guides/feature-stages.md#alpha" }}">Alpha</a></strong>,
not enabled by default, and may change in the future.</p>
<p>Use the Feature {{- if gt $len_gate 1 }}Gates{{- else}}Gate{{- end }} {{ range $index, $value := first (sub $len_gate 1) $gate }} <code>{{- $value }}</code>, {{ end }}{{ if gt $len_gate 1 }}and{{ end }}{{ range (last 1 $gate) }} <code>{{ . }}</code>{{ end }}
to enable and test {{ if gt $len_gate 1 }}these features{{else}}this feature{{ end }}.</p>
<p>See the <a href="{{ ref . "/docs/Guides/feature-stages.md#feature-gates" }}">Feature Gate documentation</a> for details on how to enable features.</p>
</div>

View File

@ -0,0 +1,9 @@
{{- $gate := .Get "gate" }}
{{- $title := .Get "title" }}
<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Warning</h4>
<p>The {{ $title }} feature is currently <strong><a href="{{ ref . "/docs/Guides/feature-stages.md#beta" }}">Beta</a></strong>,
and while it is enabled by default it may change in the future.</p>
<p>Use the Feature Gate <code>{{- $gate }}</code> to disable this feature.</p>
<p>See the <a href="{{ ref . "/docs/Guides/feature-stages.md#feature-gates" }}">Feature Gate documentation</a> for details on how to disable features.</p>
</div>

View File

@ -0,0 +1,26 @@
{{ $col_id := .Get "color" | default "dark" }}
{{/* Height can be one of: auto, min, med, max, full. */}}
{{ $height := .Get "height" | default "max" }}
<section class="row td-cover-block td-cover-block--height-{{ $height }} js-td-cover td-overlay td-overlay--light -bg-{{ $col_id }}">
<div class="container td-overlay__inner">
<div class="row align-items-end">
<div class="col-lg-6">
<h1 class="display-1 mt-0 pb-3">
<img alt="agones" width="98" src="images/logo.svg" />
Redis Operator
</h1>
<p class="display-2 mb-0">Host, Run and Scale Redis cluster on Kubernetes</p>
</div>
<div class="col-lg-6 mt-5 mt-lg-3 d-sm-block" style="display: none">
<asciinema-player
loop autoplay font-size="12px" rows=12 cols=75
src="redis-operator.cast"></asciinema-player>
</div>
<div class="col-12 pt-3">
<div class="pt-3 lead text-center">
{{ .Inner }}
</div>
</div>
</div>
</div>
</section>

View File

@ -0,0 +1 @@
{{- $.Page.Site.Params.example_image_tag }}

View File

@ -0,0 +1,40 @@
{{- $version := getenv "RELEASE_VERSION" | default $.Page.Site.Params.release_version }}
{{- with or (.Get "publishVersion") (.Get "expiryVersion")}}
{{- else }}
{{ errorf "missing value for either publishVersion or expiryVersion: %s" .Position}}
{{- end}}
{{- $publishVersion := (.Get "publishVersion" ) | default "0.0.0" }}
{{- $expiryVersion := (.Get "expiryVersion") | default "9999.0.0"}}
{{- $publDigits := split $publishVersion "." }}
{{- $curDigits := split $version "." }}
{{- $expDigits := split $expiryVersion "." }}
{{- $multiplier := 10000 }}
{{- $current := 0}}
{{- $publish := 0}}
{{- $expire := 0}}
{{- $index := 0}}
{{- /*Generate initial shift for most significant number*/}}
{{- $shift := 1 }}
{{- range $curDigits}}
{{- $shift = mul $shift $multiplier }}
{{- end}}
{{- $shift = div $shift $multiplier }}
{{- /* loop three times */}}
{{- range $curDigits}}
{{- /* Get integer from dot separated string at index */}}
{{- $c := int (index $curDigits $index)}}
{{- $p := int (index $publDigits $index)}}
{{- $e := int (index $expDigits $index)}}
{{- /* current += digit * shift */}}
{{- $current = (add $current (mul $c $shift ))}}
{{- $publish = (add $publish (mul $p $shift ))}}
{{- $expire = (add $expire (mul $e $shift ))}}
{{- $shift = (div $shift $multiplier)}}
{{- $index = (add $index 1) }}
{{- end}}
{{- if and (ge $current $publish) (lt $current $expire) }}
{{.Inner}}
{{- end}}

View File

@ -0,0 +1,3 @@
{{- $branch := .Get "branch" | default (getenv "RELEASE_BRANCH") | default $.Page.Site.Params.release_branch }}
{{- $test := eq (.Get "link_test") "true" | default true }}
<a href="{{$.Page.Site.Params.github_repo}}/blob/{{ $branch }}/{{.Get "href"}}" target="_blank" {{if not $test}}data-proofer-ignore{{end}}>{{.Inner}}</a>

View File

@ -0,0 +1,3 @@
{{- $version := .Get "version" | default (getenv "RELEASE_VERSION") | default $.Page.Site.Params.release_version }}
{{- $test := eq (.Get "link_test") "true" | default true }}
<a href="{{$.Page.Site.Params.github_repo}}/releases/download/v{{ $version }}/{{ .Get "file_prefix" }}-{{ $version }}.zip" {{if not $test}}data-proofer-ignore{{end}}>{{ .Get "file_prefix" }}-{{ $version }}.zip</a>

View File

@ -0,0 +1,14 @@
{{- $branch := .Get "branch" | default (getenv "RELEASE_BRANCH") | default $.Page.Site.Params.release_branch }}
{{- $test := eq (.Get "link_test") "true" | default true }}
<p>The following prerequisites are required to create a GameServer:</p>
<ol>
<li>A Kubernetes cluster with the UDP port range 7000-8000 open on each node.</li>
<li>Agones controller installed in the targeted cluster</li>
<li>kubectl properly configured</li>
<li>Netcat which is already installed on most Linux/macOS distributions, for windows you can use <a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10">WSL</a>.</li>
</ol>
<p>If you dont have a Kubernetes cluster you can follow <a href="/site/docs/installation/">these instructions</a> to create a cluster on Google Kubernetes Engine (GKE), Minikube or Azure Kubernetes Service (AKS), and install Agones.</p>
<p>For the purpose of this guide were going to use the
<a href="{{$.Page.Site.Params.github_repo}}/blob/{{ $branch }}/examples/simple-game-server/" target="_blank" {{if not $test}}data-proofer-ignore{{end}}>simple-game-server</a>
example as the GameServer container. This example is a very simple UDP server written in Go. Dont hesitate to look at the code of this example for more information.</p>

View File

@ -0,0 +1,2 @@
{{- $version := cond (eq (getenv "HUGO_ENV") "production") $.Page.Site.Params.supported_k8s $.Page.Site.Params.dev_supported_k8s }}
{{- $prefix := replace $version "." "-" }}https://v{{- $prefix }}.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v{{- $version }}/{{- .Get "href" }}

View File

@ -0,0 +1,5 @@
{{- if eq (getenv "HUGO_ENV") "production" }}
{{- $.Page.Site.Params.supported_k8s }}
{{- else }}
{{- $.Page.Site.Params.dev_supported_k8s }}
{{- end }}

View File

@ -0,0 +1,5 @@
{{- if eq (getenv "HUGO_ENV") "production" }}
{{- $.Page.Site.Params.minikube_minor_supported_k8s }}
{{- else }}
{{- $.Page.Site.Params.dev_minikube_minor_supported_k8s }}
{{- end }}

View File

@ -0,0 +1,2 @@
{{- $branch := getenv "RELEASE_BRANCH" | default $.Page.Site.Params.release_branch }}
{{- $branch }}

View File

@ -0,0 +1,2 @@
{{- $version := getenv "RELEASE_VERSION" | default $.Page.Site.Params.release_version }}
{{- $version }}

View File

@ -0,0 +1,2 @@
<iframe width="640" height="360" class="p-2" src="https://www.youtube.com/embed/{{ .Get 0 }}"
frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

56
main.go Normal file
View File

@ -0,0 +1,56 @@
// Copyright 2017 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
log.Printf("Defaulting to port %s", port)
}
var configPath string
switch len(os.Args) {
case 1:
configPath = "vanity.yaml"
case 2:
configPath = os.Args[1]
default:
log.Fatal("usage: govanityurls [CONFIG]")
}
log.Printf("loading: %s", configPath)
vanity, err := os.ReadFile(configPath)
if err != nil {
log.Fatal(err)
}
h, err := newHandler(vanity)
if err != nil {
log.Fatal(err)
}
http.Handle("/", h)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}
func defaultHost(r *http.Request) string {
return r.Host
}

3
netlify.toml Normal file
View File

@ -0,0 +1,3 @@
[build]
[build.environment]
HUGO_VERSION = "0.88.1"

7
package.json Normal file
View File

@ -0,0 +1,7 @@
{
"dependencies": {
"postcss-cli": "8.3.1",
"postcss": "8.2.9",
"autoprefixer": "10.2.5"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
@startuml
'https://plantuml.com/sequence-diagram
actor "Player One" as Player1
actor "Player Two" as Player2
participant Matchmaker
participant Agones
participant "GameServer\nResource" as GameServer
box "Game Server Pod"
participant "Game Server\nProcess" as Binary
participant SDK
end box
activate GameServer
== No allocated <i>GameServers</i> ==
Player1 -> Matchmaker: Requests a game session
Matchmaker -> Agones: Create: <i>GameServerAllocation</i>
note left
<i>GameServerAllocation</i> implemented to
optionally select an already allocated <i>GameServer</i>
if one exists. At this stage, one does not, so
Agones will allocate a <i>Ready</i> <i>GameServer</i>.
end note
Agones -> GameServer: Finds a <i>Ready</i> <i>GameServer</i>,\nsets it to <i>Allocated</i> State
Matchmaker <-- Agones : <i>GameServerAllocation</i> is returned\nwith <i>GameServer</i> details\nincluding IP and port to connect to.
Player1 <-- Matchmaker : Returns <i>GameServer</i> connection information
Player1 -> Binary : Connects to game server process
Binary -> SDK : SDK.Alpha.PlayerConnect(id)
note right
Process calls <i>PlayerConnect(...)</i>
on player client connection.
end note
GameServer <-- SDK : Increments <i>Status.Players.Count</i>\nand add <i>id</i> to <i>Status.Players.Ids</i>
== Allocated <i>GameServers</i> with player(s) on them ==
Player2 -> Matchmaker: Requests a game session
Matchmaker -> Agones: Create: <i>GameServerAllocation</i>
note left
The <i>GameServerAllocation</i> will this time
find the Allocated <i>GameServer</i> that "Player One"
is currently active on.
end note
Agones -> GameServer: Finds the Allocated <i>GameServer</i>\nwith player capacity.
note right
This is the same <i>GameServer</i> that "Player One"
is currently playing on.
end note
Matchmaker <-- Agones: returns <i>Allocated GameServer</i> record
Player2 <-- Matchmaker : Returns <i>GameServer</i> connection information
Player2 -> Binary : Connects to game server process
Binary -> SDK : SDK.Alpha.PlayerConnect(id)
GameServer <-- SDK : Increments <i>Status.Players.Count</i>\nand add <i>id</i> to <i>Status.Players.Ids</i>
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@ -0,0 +1,35 @@
@startuml
'https://plantuml.com/sequence-diagram
participant Matchmaker
participant Agones
participant "Canary Fleet\nResource" as CanaryFleet
participant "Stable Fleet\nResource" as StableFleet
Matchmaker -> Agones: Create: <i>GameServerAllocation</i>
note left
<i>GameServerAllocation</i> is implemented to prefer
allocating from the Canary <i>Fleet</i>, but if a Ready
<i>GameServer</i> cannot be found in the Canary Fleet,
to fall back to the Stable <i>Fleet</i>.
end note
group GameServerAllocation
else Check Canary Fleet first
Agones -> CanaryFleet: Check if <i>GameServer<i> is available
note right
The Canary <i>Fleet</i> is
usually a small, fixed size
end note
else None available in Canary Fleet
Agones -> StableFleet: Check if <i>GameServer<i> is available
note right
When the Canary <i>Fleet</i> is fully allocated, the
allocation falls over to the Stable <i>Fleet</i>.
end note
end group
Matchmaker <-- Agones: returns <i>Allocated GameServer</i> record
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -0,0 +1,67 @@
@startuml
participant Matchmaker
participant "Kubernetes API" as K8sAPI
participant Agones
participant "Game Server\nProcess" as Binary
participant SDK
participant "GameServer\nResource" as GameServer
box "Game Server Pod"
participant Binary
participant SDK
end box
== GameServer Start ==
Agones -> GameServer: GameServer created through\na <i>Fleet</i> configuration
activate GameServer
GameServer -> Binary: Agones creates a Pod with the\nconfigured Game Server Container
activate Binary
activate SDK
Binary -> SDK: SDK.Health()
note right
<i>Health()</i> is a continuous
ping that occurs under
the configured threshold.
end note
GameServer <-- SDK: Maintains Healthy status
Binary -> SDK: SDK.Ready()
note right
Call <i>Ready()</i> when the
Game Server can take player
connections and is able to
be allocated.
end note
GameServer <-- SDK: Update to <i>Ready</i> State
== Matchmaker requesting GameServer ==
Matchmaker -> K8sAPI: Create: <i>GameServerAllocation</i>
note left
Allocation atomically provides
a GameServer from a pool
of existing GameServers
(Usually a <i>Fleet</i>)
end note
K8sAPI -> Agones: Intercepts <i>GameServerAllocation</i>\nrequest
"Agones" -> GameServer: Finds a Ready <i>GameServer</i>,\nsets it to <i>Allocated</i> State
K8sAPI <-- Agones: returns <i>Allocated</i>\nGameServer record
Matchmaker <-- K8sAPI : <i>GameServerAllocation</i> is returned\nwith <i>GameServer</i> details\nincluding IP and port to connect to.
note left
If no <i>GameServer</i> can be
provided, <i>GameServerAllocation</i> is
returned with a Status
of <i>UnAllocated</i>
end note
== Players Finish Game ==
Binary -> GameServer: SDK.Shutdown()
note left
Once gameplay is complete, call
<i>Shutdown()</i> to delete the
<i>GameServer</i> resource and backing Pod.
end note
destroy Binary
destroy SDK
destroy GameServer
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

@ -0,0 +1,76 @@
@startuml
participant Matchmaker
participant Agones
participant "Game Server\nProcess" as Binary
participant "GameServer\nResource" as GameServer
participant SDK
box "Game Server Pod"
participant Binary
participant SDK
end box
== GameServer Start ==
Agones -> GameServer: GameServer created through\na <i>Fleet</i> configuration
activate GameServer
GameServer -> Binary: Agones creates a Pod with the\nconfigured Game Server Container
activate Binary
activate SDK
Binary -> SDK: SDK.Health()
note right
<i>Health()</i> is a continuous
ping that occurs under
the configured threshold.
end note
GameServer <-- SDK: Maintains Healthy status
== Match Maker Registration ==
loop
Binary -> SDK: SDK.GameServer()
note left
Matchmaker registration
could occur on a timed loop
to give each <i>GameServer</i>
time to be scaled down
while <i>Ready</i> if not allocated
end note
Binary <-- SDK: GameServer details
Binary -> SDK: SDK.Reserve(duration)
GameServer <-- SDK: Set state to <i>Reserved</i> for <i>duration</i>
note right
<i>Duration</i> should be longer
than how long the matchmaker
requires <i>Register()</i> to be
available for, so it will not be
scaled down
end note
Binary -> Matchmaker: Register()
note left
Register with the Matchmaker as
available to host a game session
with details from SDK.GameServer()
end note
end loop
== Matchmaker allocates GameServer ==
Matchmaker -> Binary: StartGameSession()
Binary -> SDK: Allocate()
note right
Disables timer to reset <i>Reserved</i> state
back to <i>Ready</i>
end note
SDK --> GameServer: Set state to <i>Allocated</i>
== Players Finish Game ==
Binary -> GameServer: SDK.Shutdown()
note left
Once gameplay is complete, call
<i>Shutdown()</i> to delete the
<i>GameServer</i> resource and backing Pod.
end note
destroy Binary
destroy SDK
destroy GameServer
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@ -0,0 +1,80 @@
digraph {
graph [fontname = "helvetica"];
node [fontname = "helvetica"];
edge [fontname = "helvetica", pad="0.2", penwidth="2"];
Created [ label = "game server created" ]
PortAllocation
Creating
Error
Starting
Scheduled
RequestReady
Ready
Reserved
Allocated
Shutdown
Unhealthy
Deleted [ label = "game server deleted" ]
Created -> PortAllocation [ label ="has any port\nwith dynamic policy", color="red" ]
Created -> Creating [ label="only static ports", color="red" ]
PortAllocation -> Creating [ label="allocated unused port", color="blue" ]
Creating -> Starting [ label="created pod", color="blue" ]
Starting -> Scheduled [ label="we have a pod, fetch its address", color="blue" ]
Scheduled -> Reserved [ label="SDK.reserved(seconds)", color="purple" ]
Scheduled -> RequestReady [ label="SDK.ready()", color="purple" ]
RequestReady -> Ready [ label="ready to be allocated", color="blue" ]
Reserved -> Ready [label="if seconds > 0 \land failed to call \lSDK.allocate() " color="purple"]
Allocated -> RequestReady [ label="SDK.ready()", color="purple" ]
Ready -> Allocated [ label="allocated for use", color="orange" ]
Ready -> Allocated [ label="SDK.allocate() ", color="purple" ]
Reserved -> Allocated [color="purple"]
Creating -> Error [ label="failed to create pod", color="blue" ]
Scheduled -> Shutdown [ label="SDK.shutdown()", color="purple" ]
RequestReady -> Shutdown [ color="purple" ]
Ready -> Shutdown [ color="purple" ]
Allocated -> Shutdown [ color="purple" ]
Reserved -> Shutdown [ color="purple" ]
Scheduled -> Unhealthy [ label="failed to call SDK.healthy()\nin a timely manner" color="purple" ]
RequestReady -> Unhealthy [ color="purple" ]
Ready -> Unhealthy [ color="purple" ]
Allocated -> Unhealthy [ color="purple" ]
Reserved -> Unhealthy [ color="purple" ]
Unhealthy -> Deleted [ label="delete unhealthy game server", color="blue" ]
Shutdown -> Deleted [ label="delete finished game server", color="blue" ]
subgraph cluster_01 {
style=invis;
{
s1 [style="invis"];
s2 [style="invis"];
s1 -> s2 [ color="red", label="API user" ]
}
{
s3 [style="invis"];
s4 [style="invis"];
s3 -> s4 [ color="purple", label="SDK" ]
}
{
s5 [style="invis"];
s6 [style="invis"];
s5 -> s6 [ color="orange", label="allocation\ncontroller" ]
}
{
s7 [style="invis"];
s8 [style="invis"];
s7 -> s8 [ color="blue", label="game server\ncontroller" ]
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

View File

@ -0,0 +1,118 @@
@startuml
participant Matchmaker
participant Agones
participant "Game Server\nProcess" as Binary
participant SDK
participant "GameServer\nResource" as GameServer
box "Game Server Pod"
participant Binary
participant SDK
end box
== GameServer Start ==
Agones -> GameServer: GameServer created through\na <i>Fleet</i> configuration
activate GameServer
GameServer -> Binary: Agones creates a Pod with the\nconfigured Game Server Container
activate Binary
activate SDK
Binary -> SDK: SDK.WatchGameServer()
note right
Use the SDK Watch function
to watch and react to allocation
events
end note
Binary -> SDK: SDK.Ready()
note right
Call <i>Ready()</i> when the
Game Server can take player
connections and is able to
be allocated.
end note
GameServer <-- SDK: Update to <i>Ready</i> State
== No allocated <i>GameServers</i> ==
Matchmaker -> Agones: Create: <i>GameServerAllocation</i>
note left
The <i>GameServerAllocation</i> is implemented to
optionally select an already allocated <i>GameServer</i>
with <i>metadata.labels["agones.dev/sdk-gs-session-ready"] = "true"</i>
if one exists. At this stage, one does not, so
Agones will allocate a <i>Ready</i> <i>GameServer</i>.
end note
Agones -> GameServer: Finds a <i>Ready</i> <i>GameServer</i>.\n\nSets <i>status.state</i> to <i>Allocated</i> State\nand <i>metadata.labels["agones.dev/sdk-gs-session-ready"] = "false"</i>\nand <i>metadata.annotations["agones.dev/last-allocated"] = current timestamp</i>
note left
By setting the label "agones.dev/sdk-gs-session-ready" to "false"
this remove the `GameServer` from possibly being
re-allocated until it knows it can handle another session.
end note
Matchmaker <-- Agones : <i>GameServerAllocation</i> is returned\nwith <i>GameServer</i> details\nincluding IP and port to connect to.
SDK --> Binary: Sends SDK.WatchGameServer()\nevent for Allocation.
note right
This initial allocation can be determined
as a change in <i>GameServer.status.state</i>
from <i>Ready</i> to <i>Allocated</i>
end note
Binary -> SDK: SDK.SetLabel("gs-session-ready", "true")
note right
Since this game process can handle <i>n</i>
concurrent sessions, set this label to match
optional allocation label selectors, so it can be
re-allocated.
(See example below for more details)
end note
SDK --> GameServer: Sets <i>metadata.labels["agones.dev/sdk-gs-session-ready"] = "true"</i>
== Allocated <i>GameServers</i> with room for more sessions ==
Matchmaker -> Agones: Create: <i>GameServerAllocation</i>
note left
The <i>GameServerAllocation</i> will this time
find the Allocated <i>GameServer</i> with the label
<i>metadata.labels["agones.dev/sdk-gs-session-ready"] = "true"</i>,
indicating that it can accept more
concurrent game sessions.
end note
Agones -> GameServer: Finds the Allocated <i>GameServer</i> with\n<i>metadata.labels["agones.dev/sdk-gs-session-ready"] = "true"</i>.\n\nSets <i>metadata.labels["agones.dev/sdk-gs-session-ready"] = "false"</i>\nand <i>metadata.annotations["agones.dev/last-allocated"] = current timestamp</i>.
note right
This is the a <i>GameServer</i> that has room
for another concurrent game session.
end note
Matchmaker <-- Agones: returns <i>Allocated GameServer</i> record
SDK --> Binary: Sends SDK.WatchGameServer()\nevent for Allocation.
note right
The game server process can watch for a change
in <i>metadata.annotations["agones.dev/last-allocated"]</i>
to determine there is an allocation event.
end note
alt <i>GameServer</i> can accept more concurrent sessions
Binary -> SDK: SDK.SetLabel("gs-session-ready", "true")
SDK --> GameServer: Sets <i>metadata.labels["agones.dev/sdk-gs-session-ready"] = "true"</i>.
end alt
note right
If the <i>GameServer</i> can accept more
concurrent sessions, reset the label
<i>"agones.dev/sdk-gs-session-ready"</i>
back to "true"
end note
== <i>GameServer</i> has completed <i>n</i> number of complete sessions ==
Binary -> SDK: SDK.Shutdown()
note left
The <i>GameServer</i> process tracks the number of sessions
that it hosts, and after <i>n</i> number, calls <i>Shutdown()</i>
to delete the <i>GameServer</i> resource and backing Pod.
end note
SDK --> GameServer: Update to <i>Shutdown</i> state.
Agones -> GameServer: Deletes GameServer resource and backing Pod.
destroy Binary
destroy SDK
destroy GameServer
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

View File

@ -0,0 +1,72 @@
@startuml
participant Matchmaker
participant Agones
participant "Game Server\nProcess" as Binary
participant SDK
participant "GameServer\nResource" as GameServer
box "Game Server Pod"
participant Binary
participant SDK
end box
== GameServer Start ==
Agones -> GameServer: GameServer created through\na <i>Fleet</i> configuration
activate GameServer
GameServer -> Binary: Agones creates a Pod with the\nconfigured Game Server Container
activate Binary
activate SDK
Binary -> SDK: SDK.Ready()
note right
Call <i>Ready()</i> when the
Game Server can take player
connections and is able to
be allocated.
end note
GameServer <-- SDK: Update to <i>Ready</i> State
== Matchmaker requesting GameServer ==
loop <i>n</i> times
Matchmaker -> Agones: Create: <i>GameServerAllocation</i>
note left
Allocation atomically provides
a GameServer from a pool
of existing GameServers
(Usually a <i>Fleet</i>)
end note
"Agones" -> GameServer: Finds a Ready <i>GameServer</i>,\nsets it to <i>Allocated</i> State
Matchmaker <-- Agones : <i>GameServerAllocation</i> is returned\nwith <i>GameServer</i> details\nincluding IP and port to connect to.
== Players Finish Game ==
Binary -> Binary: this.ResetGameServer()
note right
Resets the game server process
back to a zero state.
end note
Binary -> SDK: SDK.Ready()
note right
Process calls <i>Ready()</i> so that
the <i>GameServer</i> is moved back to
the <i>Ready</i> state, and can be allocated
once more.
end note
GameServer <-- SDK: Update to <i>Ready</i> State
end loop
== GameServer has completed <i>n</i> number of complete sessions ==
Binary -> SDK: SDK.Shutdown()
note left
The <i>GameServer</i> process tracks the number of sessions
that it hosts, and after <i>n</i> number, calls <i>Shutdown()</i>
to delete the <i>GameServer</i> resource and backing Pod.
end note
SDK --> GameServer: Update to <i>Shutdown</i> state.
Agones -> GameServer: Deletes GameServer resource\n and backing Pod.
destroy Binary
destroy SDK
destroy GameServer
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
static/favicons/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 KiB

21
static/images/logo.svg Normal file
View File

@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1000" height="1000" viewBox="0 0 1000 1000" xml:space="preserve">
<desc>Created with Fabric.js 3.5.0</desc>
<defs>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="rgba(255,255,255,0)"/>
<g transform="matrix(42.2992 0 0 42.2992 479.8228 466.7084)" id="802661">
<path style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; is-custom-font: none; font-file-url: none; fill: rgb(57,112,228); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" transform=" translate(-12, -12.1153)" d="M 11.1142 1.43752 C 11.6726 1.16175 12.3274 1.16175 12.8858 1.43752 L 19.9628 4.9333 C 20.5056 5.20144 20.8978 5.70105 21.0293 6.29205 L 22.7955 14.2294 C 22.925 14.8114 22.7882 15.4208 22.4222 15.8914 L 17.496 22.2276 C 17.1171 22.7149 16.5344 23 15.9171 23 H 8.08294 C 7.46563 23 6.88291 22.7149 6.504 22.2276 L 1.57778 15.8914 C 1.21185 15.4208 1.07497 14.8114 1.20446 14.2294 L 2.9707 6.29205 C 3.10221 5.70105 3.49436 5.20144 4.0372 4.9333 L 11.1142 1.43752 Z" stroke-linecap="round"/>
</g>
<g transform="matrix(12.921 0 0 12.921 347.0899 310.4368)" id="801018">
<path style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; is-custom-font: none; font-file-url: none; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" transform=" translate(-11.9993, -11.9995)" d="M 9.362 9.158 c 0 0 -3.16 0.35 -5.268 0.584 c -0.19 0.023 -0.358 0.15 -0.421 0.343 s 0 0.394 0.14 0.521 c 1.566 1.429 3.919 3.569 3.919 3.569 c -0.002 0 -0.646 3.113 -1.074 5.19 c -0.036 0.188 0.032 0.387 0.196 0.506 c 0.163 0.119 0.373 0.121 0.538 0.028 c 1.844 -1.048 4.606 -2.624 4.606 -2.624 s 2.763 1.576 4.604 2.625 c 0.168 0.092 0.378 0.09 0.541 -0.029 c 0.164 -0.119 0.232 -0.318 0.195 -0.505 c -0.428 -2.078 -1.071 -5.191 -1.071 -5.191 s 2.353 -2.14 3.919 -3.566 c 0.14 -0.131 0.202 -0.332 0.14 -0.524 s -0.23 -0.319 -0.42 -0.341 c -2.108 -0.236 -5.269 -0.586 -5.269 -0.586 s -1.31 -2.898 -2.183 -4.83 c -0.082 -0.173 -0.254 -0.294 -0.456 -0.294 s -0.375 0.122 -0.453 0.294 C 10.671 6.26 9.362 9.158 9.362 9.158 z" stroke-linecap="round"/>
</g>
<g transform="matrix(11.436 0 0 11.436 640.1609 321.8717)" id="323779">
<rect style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; is-custom-font: none; font-file-url: none; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" x="-8" y="-8" rx="0" ry="0" width="16" height="16"/>
</g>
<g transform="matrix(13.3101 0 0 13.3101 348.51 608.711)" id="770773">
<circle style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; is-custom-font: none; font-file-url: none; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" cx="0" cy="0" r="8"/>
</g>
<g transform="matrix(0.4498 0 0 0.4498 630.8727 595.1659)" id="874714">
<path style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; is-custom-font: none; font-file-url: none; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" transform=" translate(-256, -255.999)" d="M 464 464 H 48 a 16 16 0 0 1 -14.07 -23.62 l 208 -384 a 16 16 0 0 1 28.14 0 l 208 384 A 16 16 0 0 1 464 464 Z" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

Some files were not shown because too many files have changed in this diff Show More