/**
 * Copyright 2021 Opstrace, Inc.
 *
 * 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.
 */ import { strict as assert } from "assert" ; import { urlJoin } from "url-join-ts" ; import { ResourceCollection , Deployment , ClusterRoleBinding , ServiceAccount , Namespace , Service , Secret , withPodAntiAffinityRequired } from "@opstrace/kubernetes" ; import { KubeConfig } from "@kubernetes/client-node" ; import { State } from "../../reducer" ; import { generateSecretValue , getControllerConfig } from "../../helpers" ; import { DockerImages , getImagePullSecrets } from "@opstrace/controller-config" ; export function OpstraceApplicationResources ( state , kubeConfig , namespace , domain ) { const collection = new ResourceCollection ( ) ; const { custom_auth0_client_id , custom_auth0_domain } = getControllerConfig ( state ) ; assert ( custom_auth0_domain ) ; assert ( custom_auth0_client_id ) ; const auth0_client_id = custom_auth0_client_id ; const auth0_domain = custom_auth0_domain ; collection . add ( new Namespace ( { apiVersion : "v1" , kind : "Namespace" , metadata : { name : namespace , labels : { "cert-manager.io/disable-validation" : "true" } } } , kubeConfig ) ) ; collection . add ( new Service ( { apiVersion : "v1" , kind : "Service" , metadata : { name : "opstrace-application" , labels : { app : "opstrace-application" } , namespace } , spec : { ports : [ { name : "http" , port : 3001 , targetPort : "http" as any } ] , selector : { app : "opstrace-application" } } } , kubeConfig ) ) ; const sessionCookieSecret = new Secret ( { apiVersion : "v1" , data : { COOKIE_SECRET : Buffer . from ( generateSecretValue ( ) ) . toString ( "base64" ) } , kind : "Secret" , metadata : { name : "session-cookie-secret" , namespace : namespace } } , kubeConfig ) ; sessionCookieSecret . setImmutable ( ) ; collection . add ( sessionCookieSecret ) ; let secretValue = Buffer . from ( generateSecretValue ( ) ) . toString ( "base64" ) ; const secretToCopy = state . kubernetes . cluster . Secrets . resources . find ( secret => secret . name === "hasura-admin-secret" && secret . namespace === "kube-system" ) ; if ( secretToCopy ) { secretValue = Buffer . from ( secretToCopy . data ?. HASURA_ADMIN_SECRET ?? "" , "base64" ) . toString ( "base64" ) ; } const kubeSystemHasuraAdminSecret = new Secret ( { apiVersion : "v1" , data : { HASURA_ADMIN_SECRET : secretValue } , kind : "Secret" , metadata : { name : "hasura-admin-secret" , namespace : "kube-system" } } , kubeConfig ) ; kubeSystemHasuraAdminSecret . setImmutable ( ) ; collection . add ( kubeSystemHasuraAdminSecret ) ; const hasuraAdminSecret = new Secret ( { apiVersion : "v1" , data : { HASURA_ADMIN_SECRET : secretValue } , kind : "Secret" , metadata : { name : "hasura-admin-secret" , namespace : namespace } } , kubeConfig ) ; hasuraAdminSecret . setImmutable ( ) ; collection . add ( hasuraAdminSecret ) ; const hasuraActionSecret = new Secret ( { apiVersion : "v1" , data : { HASURA_CONFIG_API_SECRET : Buffer . from ( generateSecretValue ( ) ) . toString ( "base64" ) } , kind : "Secret" , metadata : { name : "hasura-action-secret" , namespace : namespace } } , kubeConfig ) ; hasuraActionSecret . setImmutable ( ) ; collection . add ( hasuraActionSecret ) ; collection . add ( new Deployment ( { apiVersion : "apps/v1" , kind : "Deployment" , metadata : { name : "opstrace-application" , namespace , labels : { app : "opstrace-application" } } , spec : { replicas : 1 , strategy : { type : "RollingUpdate" , rollingUpdate : { maxSurge : "25%" as any , maxUnavailable : "25%" as any } } , selector : { matchLabels : { app : "opstrace-application" } } , template : { metadata : { labels : { app : "opstrace-application" } } , spec : { imagePullSecrets : getImagePullSecrets ( ) , serviceAccountName : "opstrace-application" , terminationGracePeriodSeconds : 80 , affinity : withPodAntiAffinityRequired ( { app : "opstrace-application" } ) , containers : [ { name : "opstrace-application" , image : DockerImages . app , imagePullPolicy : "IfNotPresent" , command : [ "node" , "dist/server.js" ] , env : [ { name : "REDIS_HOST" , value : `redis-master.${ namespace }.svc.cluster.local` } , { name : "REDIS_PASSWORD" , valueFrom : { secretKeyRef : { name : "redis-password" , key : "REDIS_MASTER_PASSWORD" } } } , { name : "GRAPHQL_ENDPOINT_HOST" , value : `graphql.${ namespace }.svc.cluster.local` } , { name : "GRAPHQL_ENDPOINT_PORT" , value : "8080" } , { name : "GRAPHQL_ENDPOINT" , value : `http://graphql.${ namespace }.svc.cluster.local:8080/v1/graphql` } , { name : "AUTH0_CLIENT_ID" , value : auth0_client_id } , { name : "AUTH0_DOMAIN" , value : auth0_domain } , { name : "DOMAIN" , value : `https://${ domain }` } , { name : "UI_DOMAIN" , value : `https://${ domain }` } , { name : "COOKIE_SECRET" , valueFrom : { secretKeyRef : { name : "session-cookie-secret" , key : "COOKIE_SECRET" } } } , { name : "HASURA_GRAPHQL_ADMIN_SECRET" , valueFrom : { secretKeyRef : { name : "hasura-admin-secret" , key : "HASURA_ADMIN_SECRET" } } } ] , ports : [ { containerPort : 3001 , name : "http" } ] , resources : { } , readinessProbe : { httpGet : { path : "/ready" , port : 9000 as any , scheme : "HTTP" } , failureThreshold : 1 , initialDelaySeconds : 5 , periodSeconds : 5 , successThreshold : 1 , timeoutSeconds : 5 } , livenessProbe : { httpGet : { path : "/live" , port : 9000 as any , scheme : "HTTP" } , failureThreshold : 3 , initialDelaySeconds : 5 , periodSeconds : 30 , successThreshold : 1 , timeoutSeconds : 5 } , startupProbe : { httpGet : { path : "/live" , port : 9000 as any } , failureThreshold : 3 , initialDelaySeconds : 10 , periodSeconds : 30 , successThreshold : 1 , timeoutSeconds : 5 } } ] } } } } , kubeConfig ) ) ; collection . add ( new Service ( { apiVersion : "v1" , kind : "Service" , metadata : { name : "graphql" , labels : { app : "graphql" } , namespace } , spec : { ports : [ { name : "http" , port : 8080 , targetPort : "http" as any } ] , selector : { app : "graphql" } } } , kubeConfig ) ) ; collection . add ( new Deployment ( { apiVersion : "apps/v1" , kind : "Deployment" , metadata : { name : "graphql" , namespace , labels : { app : "graphql" } } , spec : { replicas : 1 , strategy : { type : "RollingUpdate" , rollingUpdate : { maxSurge : "25%" as any , maxUnavailable : "25%" as any } } , selector : { matchLabels : { app : "graphql" } } , template : { metadata : { labels : { app : "graphql" } } , spec : { imagePullSecrets : getImagePullSecrets ( ) , affinity : withPodAntiAffinityRequired ( { app : "graphql" } ) , containers : [ { name : "graphql" , image : DockerImages . graphqlEngine , imagePullPolicy : "IfNotPresent" , env : [ { name : "HASURA_GRAPHQL_ADMIN_SECRET" , valueFrom : { secretKeyRef : { name : "hasura-admin-secret" , key : "HASURA_ADMIN_SECRET" } } } , { name : "HASURA_GRAPHQL_DATABASE_URL" , value : urlJoin ( state . config . config ?. postgreSQLEndpoint , state . config . config ?. opstraceDBName ) } , { name : "HASURA_GRAPHQL_ENABLE_CONSOLE" , value : "false" } , { name : "HASURA_GRAPHQL_ENABLED_LOG_TYPES" , value : "startup, http-log, websocket-log" } , { name : "ACTION_CONFIG_API_ENDPOINT" , value : `http://opstrace-api.${ namespace }.svc.cluster.local:8081` } , { name : "ACTION_CONFIG_API_SECRET" , valueFrom : { secretKeyRef : { name : "hasura-action-secret" , key : "HASURA_CONFIG_API_SECRET" } } } ] , ports : [ { containerPort : 8080 , name : "http" } ] , resources : { } } ] } } } } , kubeConfig ) ) ; collection . add ( new ServiceAccount ( { apiVersion : "v1" , kind : "ServiceAccount" , metadata : { name : "opstrace-application" , namespace } } , kubeConfig ) ) ; collection . add ( new ClusterRoleBinding ( { apiVersion : "rbac.authorization.k8s.io/v1" , kind : "ClusterRoleBinding" , metadata : { name : "opstrace-application-clusteradmin-binding" } , roleRef : { apiGroup : "rbac.authorization.k8s.io" , kind : "ClusterRole" , name : "cluster-admin" } , subjects : [ { kind : "ServiceAccount" , name : "opstrace-application" , namespace } ] } , kubeConfig ) ) ; return collection ; }