Skip to content

Commit 6f09633

Browse files
authored
Merge pull request #3990 from uselagoon/api-defined-routes
feat: support for routes defined in the api
2 parents 8448b56 + b916407 commit 6f09633

File tree

26 files changed

+3368
-23
lines changed

26 files changed

+3368
-23
lines changed

Jenkinsfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ pipeline {
121121
parallel {
122122
stage ('1: run first test suite') {
123123
steps {
124-
sh script: "make -j$NPROC k3d/retest TESTS=[api,deploytarget,active-standby-kubernetes,features-kubernetes,features-kubernetes-2,features-variables] BRANCH_NAME=${SAFEBRANCH_NAME}", label: "Running first test suite on k3d cluster"
124+
sh script: "make -j$NPROC k3d/retest TESTS=[api,api-routes,deploytarget,active-standby-kubernetes,features-kubernetes,features-kubernetes-2,features-variables] BRANCH_NAME=${SAFEBRANCH_NAME}", label: "Running first test suite on k3d cluster"
125125
sh script: "pkill -f './local-dev/stern'", label: "Closing off test-suite-1 log after test completion"
126126
}
127127
}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ JWT_VERSION = 6.2.0
462462
STERN_VERSION = v2.6.1
463463
CHART_TESTING_VERSION = v3.11.0
464464
K3D_IMAGE = docker.io/rancher/k3s:v1.31.1-k3s1
465-
TESTS = [nginx,api,features-kubernetes,bulk-deployment,features-kubernetes-2,features-variables,active-standby-kubernetes,tasks,drush,python,gitlab,github,bitbucket,services]
465+
TESTS = [nginx,api,api-routes,features-kubernetes,bulk-deployment,features-kubernetes-2,features-variables,active-standby-kubernetes,tasks,drush,python,gitlab,github,bitbucket,services]
466466
CHARTS_TREEISH = main
467467
CHARTS_REPOSITORY = https://github.com/uselagoon/lagoon-charts.git
468468
#CHARTS_REPOSITORY = ../lagoon-charts

node-packages/commons/src/api.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,26 @@ export interface Project {
4343
standbyRoutes: string;
4444
storageCalc: number;
4545
subfolder: string;
46+
apiRoutes: any[];
47+
autogeneratedRouteConfig: AutogeneratedRouteConfig;
48+
}
49+
50+
export interface AutogeneratedRouteConfig {
51+
updated: string;
52+
pathRoutes: AutogeneratedPathRoutes[];
53+
disableRequestVerification: boolean;
54+
enabled?: boolean;
55+
allowPullRequests?: boolean;
56+
insecure?: string;
57+
prefixes?: string[];
58+
tlsAcme?: boolean;
59+
// ingressClass?: string;
60+
}
61+
62+
export interface AutogeneratedPathRoutes {
63+
toService: string;
64+
fromService: string;
65+
path: string;
4666
}
4767

4868
export interface Kubernetes {
@@ -681,6 +701,51 @@ export const allProjectsInGroup = (groupInput: {
681701
}
682702
);
683703

704+
705+
const apiRouteFragment = graphqlapi.createFragment(`
706+
fragment on Route {
707+
domain
708+
service
709+
alternativeNames{
710+
domain
711+
}
712+
annotations{
713+
key
714+
value
715+
}
716+
pathRoutes{
717+
toService
718+
path
719+
}
720+
tlsAcme
721+
insecure
722+
hstsEnabled
723+
hstsIncludeSubdomains
724+
hstsPreload
725+
hstsMaxAge
726+
primary
727+
source
728+
type
729+
}
730+
`);
731+
732+
const autogeneratedRouteConfigFragment = graphqlapi.createFragment(`
733+
fragment on AutogeneratedRouteConfig {
734+
updated
735+
enabled
736+
allowPullRequests
737+
prefixes
738+
tlsAcme
739+
insecure
740+
pathRoutes {
741+
toService
742+
fromService
743+
path
744+
}
745+
disableRequestVerification
746+
}
747+
`);
748+
684749
export async function getEnvironmentByName(
685750
name: string,
686751
projectId: number,
@@ -697,9 +762,23 @@ export async function getEnvironmentByName(
697762
autoIdle,
698763
environmentType,
699764
openshiftProjectName,
765+
openshift {
766+
...${deployTargetMinimalFragment}
767+
}
700768
updated,
701769
created,
702770
deleted,
771+
envVariables {
772+
name
773+
value
774+
scope
775+
}
776+
apiRoutes(source: API){
777+
...${apiRouteFragment}
778+
}
779+
autogeneratedRouteConfig {
780+
...${autogeneratedRouteConfigFragment}
781+
}
703782
}
704783
}
705784
`);
@@ -733,6 +812,12 @@ export async function getEnvironmentByIdWithVariables(
733812
value
734813
scope
735814
}
815+
apiRoutes(source: API){
816+
...${apiRouteFragment}
817+
}
818+
autogeneratedRouteConfig {
819+
...${autogeneratedRouteConfigFragment}
820+
}
736821
}
737822
}
738823
`);
@@ -846,6 +931,8 @@ interface GetOpenshiftInfoForProjectResult {
846931
| 'standbyRoutes'
847932
| 'storageCalc'
848933
| 'subfolder'
934+
| 'apiRoutes'
935+
| 'autogeneratedRouteConfig'
849936
> & {
850937
openshift: Pick<Kubernetes, keyof DeployTargetMinimalFragment>;
851938
envVariables: Pick<EnvKeyValue, 'name' | 'scope' | 'value'>[];
@@ -885,6 +972,12 @@ export const getOpenShiftInfoForProject = (project: string): Promise<GetOpenshif
885972
standbyRoutes
886973
storageCalc
887974
subfolder
975+
apiRoutes{
976+
domain
977+
}
978+
autogeneratedRouteConfig {
979+
...${autogeneratedRouteConfigFragment}
980+
}
888981
}
889982
}
890983
`);

node-packages/commons/src/tasks.ts

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ import {
2020
addDeployment,
2121
getOrganizationByIdWithEnvs,
2222
getOrganizationById,
23+
AutogeneratedRouteConfig,
2324
} from './api';
2425
import {
2526
deployTargetBranches,
2627
deployTargetPullrequest,
2728
deployTargetPromote
2829
} from './deploy-tasks';
29-
import { InternalEnvVariableScope, DeployData, DeployType, RemoveData } from './types';
30+
import { InternalEnvVariableScope, DeployData, DeployType, RemoveData, RouteSource } from './types';
3031
// @ts-ignore
3132
import sha1 from 'sha1';
3233
import crypto from 'crypto';
@@ -528,11 +529,10 @@ export const getControllerBuildData = async function(deployTarget: any, deployDa
528529
}
529530

530531
let deployment;
531-
let environmentId;
532+
const now = moment.utc();
533+
const apiEnvironment = await getEnvironmentByName(branchName, lagoonProjectData.id, false);
534+
const environmentId = apiEnvironment.environmentByName.id
532535
try {
533-
const now = moment.utc();
534-
const apiEnvironment = await getEnvironmentByName(branchName, lagoonProjectData.id, false);
535-
environmentId = apiEnvironment.environmentByName.id
536536
deployment = await addDeployment(buildName,
537537
"NEW",
538538
now.format('YYYY-MM-DDTHH:mm:ss'),
@@ -551,10 +551,10 @@ export const getControllerBuildData = async function(deployTarget: any, deployDa
551551
// encode some values so they get sent to the controllers nicely
552552
const { routerPattern, appliedEnvVars: envVars } = await getEnvironmentsRouterPatternAndVariables(
553553
lagoonProjectData,
554-
environment.addOrUpdateEnvironment,
554+
apiEnvironment.environmentByName,
555555
deployTarget.openshift,
556556
bulkId || null, bulkName || null, priority, buildVariables,
557-
bulkType.Deploy
557+
bulkType.Deploy,
558558
)
559559

560560
let organization: any = null;
@@ -627,23 +627,75 @@ enum bulkType {
627627
export const getEnvironmentsRouterPatternAndVariables = async function(
628628
project: Pick<
629629
Project,
630-
'routerPattern' | 'sharedBaasBucket' | 'organization'
630+
'routerPattern' |
631+
'sharedBaasBucket' |
632+
'organization' |
633+
'apiRoutes' |
634+
'autogeneratedRouteConfig'
631635
> & {
632636
openshift: Pick<Kubernetes, 'routerPattern'>;
633637
envVariables: Pick<EnvKeyValue, 'name' | 'value' | 'scope'>[];
634638
},
635-
environment: { envVariables: Pick<EnvKeyValue, 'name' | 'value' | 'scope'>[]},
639+
environment: {
640+
envVariables: Pick<EnvKeyValue, 'name' | 'value' | 'scope'>[]
641+
apiRoutes,
642+
autogeneratedRouteConfig,
643+
},
636644
deployTarget: Pick<Kubernetes, 'name' | 'routerPattern' | 'sharedBaasBucketName'>,
637645
bulkId: string | null,
638646
bulkName: string | null,
639647
buildPriority: number,
640648
buildVariables: Array<{name: string, value: string}>,
641-
bulkTask: bulkType
649+
bulkTask: bulkType,
642650
): Promise<{ routerPattern: string, appliedEnvVars: string }> {
643651
type EnvKeyValueInternal = Pick<EnvKeyValue, 'name' | 'value'> & {
644652
scope: EnvVariableScope | InternalEnvVariableScope;
645653
}
646654

655+
/*
656+
prepares the values to send to builds and tasks for consumption
657+
this ensures the data is structured to match the one that the build expects when it receives the payload
658+
this gets based64 encoded before being shipped. it is decoded during the build
659+
*/
660+
let apiRoutes = environment.apiRoutes
661+
if (apiRoutes.length > 0) {
662+
for (let i = 0; i < apiRoutes.length; i++) {
663+
// remove any yaml/autogenerated sourced apiRoutes from the list
664+
// these don't get send to builds from the api as they are configured by lagoon.yml
665+
// or are created by the autogenerated route configuration
666+
if ([RouteSource.YAML, RouteSource.AUTOGENERATED].includes(apiRoutes[i].source.toLowerCase())) {
667+
// the environment queries have (source: API) on the environments apiRoutes request
668+
// this check is just a fallback check
669+
apiRoutes.splice(i, 1);
670+
i--;
671+
continue;
672+
}
673+
// the structure of annotations in the build-deploy-tool are `map[string]string`
674+
// this converts the `key:value` type from the api to `map[string]string` for the build-deploy-tool to consume
675+
let annotations = apiRoutes[i].annotations.reduce((acc, item) => {
676+
acc[item.key] = item.value;
677+
return acc;
678+
}, {});
679+
apiRoutes[i].annotations = annotations
680+
// the structure of alternativeNames in the build-deploy-tool are `[]string`
681+
// this converts to that type for the build-deploy-tool to consume
682+
let alternativeNames = apiRoutes[i].alternativeNames.map(item => item.domain);
683+
apiRoutes[i].alternativeNames = alternativeNames
684+
}
685+
}
686+
687+
/*
688+
this handles creating the autogenerated route configuration if any autogenerated route settings
689+
on the project or environment are set, this then gets setup in the required format
690+
and base64 encoded before being sent to the build, it is decoded there
691+
*/
692+
let agPayload = {}
693+
if (project.autogeneratedRouteConfig !== null) {
694+
agPayload = project.autogeneratedRouteConfig
695+
}
696+
if (environment.autogeneratedRouteConfig !== null) {
697+
agPayload = environment.autogeneratedRouteConfig
698+
}
647699
// Single list of env vars that apply to an environment. Env vars from multiple
648700
// sources (organization, project, enviornment, build vars, etc) are
649701
// consolidated based on precedence.
@@ -711,6 +763,37 @@ export const getEnvironmentsRouterPatternAndVariables = async function(
711763
});
712764
}
713765

766+
// `LAGOON_API_ROUTES` is the successor to LAGOON_ROUTES_JSON. the build-deploy-tool will
767+
// check if `LAGOON_ROUTES_JSON` is defined, then that will be used to prevent breaking deployments
768+
// but will produce a build warning indicating that LAGOON_ROUTES_JSON will be deprecated in the future
769+
// and to use API defined routes instead
770+
if (apiRoutes.length > 0) {
771+
applyIfNotExists({
772+
name: "LAGOON_API_ROUTES",
773+
value: encodeJSONBase64({routes: apiRoutes}),
774+
scope: InternalEnvVariableScope.INTERNAL_SYSTEM
775+
});
776+
}
777+
if (project.apiRoutes.length > 0) {
778+
// if the project has any apiroutes defined, then we enforce cleanup of removed routes on any environments
779+
applyIfNotExists({
780+
name: "LAGOON_API_ROUTES_CLEANUP",
781+
value: "true",
782+
scope: InternalEnvVariableScope.INTERNAL_SYSTEM
783+
});
784+
}
785+
if (Object.keys(agPayload).length) {
786+
// if there is any autogenerated route configuration to send
787+
// set that here
788+
// @TODO: need to figure out a better way to handle the default autogenerate insecure handling
789+
// agPayload.autogenerate.insecure = "Redirect"
790+
applyIfNotExists({
791+
name: "LAGOON_API_AUTOGENERATED_CONFIG",
792+
value: encodeJSONBase64(agPayload),
793+
scope: InternalEnvVariableScope.INTERNAL_SYSTEM
794+
});
795+
}
796+
714797
/*
715798
* Normally scoped env vars.
716799
*
@@ -1076,7 +1159,7 @@ export const getTaskProjectEnvironmentVariables = async (projectName: string, en
10761159
result.project,
10771160
environment.environmentById,
10781161
environment.environmentById.openshift,
1079-
null, null, priority, [], bulkType.Task // bulk deployments don't apply to tasks yet, but this is future proofing the function call
1162+
null, null, priority, [], bulkType.Task, // bulk deployments don't apply to tasks yet, but this is future proofing the function call
10801163
)
10811164
return appliedEnvVars
10821165
}

node-packages/commons/src/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ export enum AuditType {
5858
FILE = 'file',
5959
}
6060

61+
export enum RouteType {
62+
STANDARD = 'standard',
63+
STANDBY = 'standby',
64+
ACTIVE = 'active'
65+
}
66+
67+
export enum RouteSource {
68+
API = 'api',
69+
YAML = 'yaml',
70+
AUTOGENERATED = 'autogenerated'
71+
}
72+
6173
export interface DeployData {
6274
baseBranchName?: string,
6375
baseSha?: string,

0 commit comments

Comments
 (0)