#!/usr/bin/env bash

# Copyright 2022 The KCP Authors.
#
# 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.

TYPE_SPEED=${TYPE_SPEED:-150}
#NO_WAIT=true

DEMO_DIR="$(dirname "${BASH_SOURCE[0]}")"
source "${DEMO_DIR}"/../.setupEnv

# shellcheck source=../demo-magic
. "${DEMOS_DIR}"/demo-magic

DEMO_PROMPT="☸️ $ "

function pause() {
  if [[ "${NO_WAIT}" = "true" ]]; then
    sleep 2
  else
    if [[ -n "${1-}" ]]; then
      sleep "$1"
    else
      wait
    fi
  fi
}

function c() {
  local comment="$*"
  if command -v fold &> /dev/null; then
    comment=$(echo "$comment" | fold -w "${cols:-100}")
  fi
  p "# $comment"
}

export KUBECONFIG=${KUBECONFIG:-${KCP_DIR}/.kcp/admin.kubeconfig}

if ! kubectl get namespaces &>/dev/null; then
  echo "kcp server not started, run 'bin/kcp start'"
  exit 1
fi

clear

c "Install nginx-based ingress into both kind clusters"

pe "kubectl --kubeconfig ${CLUSTERS_DIR}/us-east1.kubeconfig apply -f ${DEMOS_DIR}/ingress-script/nginx-ingress.yaml"
pe "kubectl --kubeconfig ${CLUSTERS_DIR}/us-west1.kubeconfig apply -f ${DEMOS_DIR}/ingress-script/nginx-ingress.yaml"

wait
clear

c "First let's explore a little about the kcp server and the workspaces."
pe "kubectl config current-context"
c "We are now in the default organization, created on kcp startup. There is not much in here, not even a namespace. But we can create it."
pe "kubectl create namespace default"

c "We can switch over to the root workspace to see all organizations on this kcp."
pe "kubectl config use-context root"
pe "kubectl get clusterworkspaces"
c "We are alone. Let's create an organization called demo, to not use the default organization as playground."
cat <<EOF > "${KCP_DATA_DIR}/demo-org.yaml"
apiVersion: tenancy.kcp.io/v1alpha1
kind: ClusterWorkspace
metadata:
  name: demo
spec:
  type: Organization
EOF
pe "cat ${KCP_DATA_DIR}/demo-org.yaml"
pe "kubectl create -f ${KCP_DATA_DIR}/demo-org.yaml"
c "Let's check that it is created and becoming ready."
pe "kubectl get clusterworkspaces"

c "We can switch over to the demo organization. For this, we could add the URL to our kubeconfig. But this far too complicate. We have this awesome kcp kubectl plugin:"
pe "kubectl kcp workspace use demo"

wait
clear

c "We will now real non-admin users. So we have to first given them member permission for the demo organization."
pe "cat ${DEMO_DIR}/user-rbac.yaml"
pe "kubectl apply --context root -f ${DEMO_DIR}/user-rbac.yaml"

c "Let's start by creating a \"real\" workspace and using it. That's what organization workspace are for, to hold the actual application workspaces."
pe "kubectl kcp workspace --token user-1-token create workspace1 --use"

c "Because KCP doesn't include Deployments by default, and we haven't registered any Clusters yet, we need to teach KCP about Deployments"

c "First grab the schema for deployments from one of our kind clusters"
pe "(cd ${KCP_DATA_DIR} && go run ${KCP_DIR}/cmd/crd-puller/pull-crds.go --kubeconfig ${CLUSTERS_DIR}/us-east1.kubeconfig deployments.apps)"

c "Now apply it to kcp"
pe "kubectl apply -f ${KCP_DATA_DIR}/deployments.apps.yaml"

wait
clear

c "Now we can create a deployment"
pe "kubectl create namespace default"

# Use the right arch for the kuard image
kuardArch=$(uname -m)
if [[ "${kuardArch}" == "x86_64" ]]; then
  kuardArch=amd64
fi
sed "s/ARCH/$kuardArch/" "${DEMO_DIR}"/deployment-kuard.yaml > "${KCP_DATA_DIR}"/deployment-kuard.yaml

pe "cat ${KCP_DATA_DIR}/deployment-kuard.yaml"

pe "kubectl apply -f ${KCP_DATA_DIR}/deployment-kuard.yaml"

wait
clear

c "Let's describe it to see its status - take note of Replicas"
pe "kubectl describe deployment/kuard"

wait
clear
c "Now let's register our kind cluster us-east1 with kcp"

cat <<EOF > "${KCP_DATA_DIR}/cluster-us-east1.yaml"
apiVersion: workload.kcp.io/v1alpha1
kind: SyncTarget
metadata:
  name: kind-us-east1
spec:
  kubeconfig: |
$(sed 's,^,    ,' "${CLUSTERS_DIR}"/us-east1.kubeconfig)
EOF
pe "cat ${KCP_DATA_DIR}/cluster-us-east1.yaml"

wait;clear
pe "kubectl apply -f ${KCP_DATA_DIR}/cluster-us-east1.yaml"

c "Let's wait for kcp to have the cluster syncing ready"
pe "kubectl wait --for condition=Ready workloadcluster/kind-us-east1"

c "Let's wait for the deployment to be running in kind"
pe "while ! kubectl --kubeconfig ${CLUSTERS_DIR}/us-east1.kubeconfig wait --for=jsonpath='{.status.availableReplicas}'=1 -A -l 'workloads.kcp.io/cluster=kind-us-east1' --field-selector 'metadata.name=kuard' deployments; do
  sleep 1
done"

wait
clear

c "Let's describe the deployment again"
pe "kubectl describe deployment/kuard"

c "Wait for the Services API resource to be synced from kind"
pe "while ! kubectl api-resources | grep 'services.*svc.*Service'; do sleep 1; done"

c "Let's expose the deployment"
pe "kubectl expose deployment/kuard"

wait;clear
c "And create an ingress"
pe "cat ${DEMO_DIR}/ingress-kuard.yaml"
pe "kubectl apply -f ${DEMO_DIR}/ingress-kuard.yaml"

c "Wait for the ingress to be accepted by the ingress controller in the us-east1 cluster"
pe "while ! kubectl get ingress -l ingress.kcp.io/envoy=true -o=jsonpath='{.items[*].status.loadBalancer.ingress[*].ip}' | grep -qoE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b'; do sleep 1; done"

c "Now let's access our application with curl - we should get a 200 status code"
pe "curl -s -o /dev/null --write-out '%{http_code}' -H 'Host: kuard.kcp-apps.127.0.0.1.nip.io' localhost:8181"

wait;clear

c "Now let's register our kind cluster us-west1 with kcp"

cat <<EOF > "${KCP_DATA_DIR}/cluster-us-west1.yaml"
apiVersion: workload.kcp.io/v1alpha1
kind: SyncTarget
metadata:
  name: kind-us-west1
spec:
  kubeconfig: |
$(sed 's,^,    ,' "${CLUSTERS_DIR}"/us-west1.kubeconfig)
EOF
pe "cat ${KCP_DATA_DIR}/cluster-us-west1.yaml"

wait;clear
pe "kubectl apply -f ${KCP_DATA_DIR}/cluster-us-west1.yaml"

c "Let's delete kind-us-east1 and see what happens"
pe "kind delete clusters us-east1"

c "Force the cluster healthcheck to run (we'll be improving this so it's not manual in the next prototype!)"
pe "kubectl label workloadcluster/kind-us-east1 --overwrite now=\"$(date +%s)\""
c "Show the cluster's status - us-east1 should be not ready"
pe "kubectl get workloadclusters -o wide"

wait
clear

c "Let's see if the deployment got rescheduled"
pe "kubectl describe deployment/kuard"

c "Let's wait for the ingress to be accepted by the ingress controller in the us-west1 cluster"
pe "while ! kubectl get ingress -l ingress.kcp.io/envoy=true -o=jsonpath='{.items[*].status.loadBalancer.ingress[*].ip}' | grep -qoE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b'; do sleep 1; done"
c "And now, lets make an http request to our application again"
pe "curl -s -o /dev/null --write-out '%{http_code}' -H 'Host: kuard.kcp-apps.127.0.0.1.nip.io' localhost:8181"

#### Section 3 Policy/Orgs
wait
clear

c "Now we're going to show our new workspaces APIs! Think of a workspace like your own Kubernetes apiserver, where you can create your own CRDs without worrying about conflicting with anyone else's."
c "Let's create a new workspace called 'playground' as user-1 and switch to it."
pe "kubectl kcp workspace --token user-1-token create playground --use"

c "This sets up RBAC permissions, including a ClusterRole for owners of the workspace, and a ClusterRoleBinding for user-1 as an owner:"
pe "kubectl --context workspace.kcp.io/personal/demo get clusterrole/owner-workspace-playground-user-1 -oyaml"
wait
clear
pe "kubectl --context workspace.kcp.io/personal/demo get clusterrole/owner-workspace-playground-user-1 -oyaml"
wait
clear
c "As user-1, I should be able to access content in my new workspace"
pe "kubectl get namespaces"
pe "kubectl create namespace default"
pe "kubectl create configmap test --from-literal hello=world"

c "And if we remove user-1's access by deleting the ClusterRoleBinding..."
pe "kubectl --context workspace.kcp.io/personal/demo delete clusterrolebinding owner-workspace-playground-user-1"
c "Then we see permissions have been revoked"
pe "kubectl get configmaps"
pe "kubectl get namespaces"
wait;clear
c "Since we only removed permission to the playground workspace, user-1 should still be able to use workspace1 (created earlier)"
pe "kubectl kcp workspace --token user-1-token use workspace1"
pe "kubectl get namespaces"
wait;clear
c "Each workspace maps to a specific path in the kcp apiserver. For workspace1 (part of the demo org), its content is stored in /clusters/demo:workspace1"
pe "kubectl get --raw /clusters/demo:workspace1/api/v1/namespaces"
wait;clear
c "And you can't get access to a path for a workspace until that workspace has been created:"
pe "kubectl get --raw /clusters/demo:someotherworkspace/api/v1/namespaces"

#### Section 4 API Inheritance
wait;clear

c "Now let's examine one more cool feature of workspaces - API inheritance! A workspace can specify another workspace from which it can inherit CRD-based APIs. Let's start by creating 2 workspaces that don't have any inheritance."

c "Add a workspace called 'inherit' for user-2 (but it's not inheriting yet!)"
pe "kubectl kcp workspace --token user-2-token create inherit"
c "Add a workspace called 'source' for user-1 and use it"
pe "kubectl kcp workspace --token user-1-token create source --use"

c "Let's add a CRD to the source workspace and show that it's only available there and not in the inheriting workspace yet"
pe "kubectl apply -f ${KCP_DIR}/test/e2e/fixtures/wildwest/wildwest.dev_cowboys.yaml"

c "The CRD and API resources exist:"
pe "kubectl get crd"
pe "kubectl api-resources | grep cowboys"

wait;clear
c "Let's create a Cowboy in 'source'"

pe "kubectl create namespace default"

c "Create a Cowboy in source"
pe "cat <<EOF | kubectl apply -f -
apiVersion: wildwest.dev/v1alpha1
kind: Cowboy
metadata:
  name: bob
spec:
  intent: Peaceful
EOF"

p "Show that it was created"
pe "kubectl get cowboys"

wait;clear
c "Switch to the inherit workspace and show there are no CRDs or Cowboys"
pe "kubectl kcp workspace --token user-2-token use inherit"
pe "kubectl get crd"
pe "kubectl api-resources | grep cowboys"
pe "kubectl get cowboys"

wait;clear
c "Update the inherit workspace to inherit from source"
pe "kubectl --context workspace.kcp.io/personal/demo patch clusterworkspaces/inherit --type merge --patch '{\"spec\":{\"inheritFrom\":\"source\"}}'"

c "Now we have access to the cowboys resource"
pe "kubectl api-resources | grep cowboys"

c "But we don't see it as a CRD"
pe "kubectl get crd"

wait;clear
c "Now let's work with cowboys via inheritance"
pe "kubectl create namespace default"

c "Create a Cowboy in inherit"
pe "cat <<EOF | kubectl apply -f -
apiVersion: wildwest.dev/v1alpha1
kind: Cowboy
metadata:
  name: via-inheritance
spec:
  intent: Happy
EOF"

c "Show it exists"
pe "kubectl get cowboys"

c "Check source and make sure it only has bob"
pe "kubectl kcp workspace --token user-1-token use source"
pe "kubectl get cowboys"

c "Shows: we did some exploratory work to show we can inject types into a workspace without it existing as a CRD in the workspace - in the future this will be more even more powerful"
