Skip to content
This repository has been archived by the owner on Jul 12, 2022. It is now read-only.

Butler operator health status #912

Merged
merged 8 commits into from
Feb 22, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ export class ComponentsRepositoryV2 extends Repository<ComponentEntityV2> {
.getMany()
}

public async findHealthyActiveComponents(cdConfigurationId: string): Promise<ComponentEntityV2[]> {
// WARNING: ALWAYS RETURN COMPONENT WITH ITS DEPLOYMENT
return this.createQueryBuilder('v2components')
.leftJoinAndSelect('v2components.deployment', 'deployment')
.where('deployment.current = true')
.andWhere('deployment.healthy = true')
.andWhere('deployment.cd_configuration_id = :cdConfigurationId', { cdConfigurationId })
.orderBy('deployment.created_at', 'DESC')
.getMany()
}

public async findDefaultActiveComponents(defaultCircleId: string): Promise<ComponentEntityV2[]> {
// WARNING: ALWAYS RETURN COMPONENT WITH ITS DEPLOYMENT
return this.createQueryBuilder('v2components')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,17 @@ export class DeploymentRepositoryV2 extends Repository<DeploymentEntityV2> {
.where({ id: id })
.returning('id')
.execute()
return this.findOneOrFail(updated.raw[0].id)
return await this.findOneOrFail(updated.raw[0].id)
}
public async updateRouteStatus(id: string, status: boolean): Promise<DeploymentEntityV2> {

public async updateRouteStatus(circleId: string, status: boolean): Promise<DeploymentEntityV2> {
const updated = await this.createQueryBuilder('d')
.update()
.set({ routed: status })
.where({ id: id })
.where({ circleId: circleId, current: true })
.returning('id')
.execute()
return this.findOneOrFail(updated.raw[0].id)
return await this.findOneOrFail(updated.raw[0].id)
}

public async findWithComponentsAndConfig(deploymentId: string): Promise<DeploymentEntityV2> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class CreateUndeploymentUseCase {
private async createExecution(deployment: DeploymentEntity, incomingCircleId: string | null): Promise<Execution> {
this.consoleLoggerService.log('START:CREATE_UNDEPLOYMENT_EXECUTION', { deployment: deployment.id })
const execution = await this.executionRepository.save({ deployment, type: ExecutionTypeEnum.UNDEPLOYMENT, incomingCircleId })
await this.deploymentsRepository.update({ id: deployment.id }, { current: false })
await this.deploymentsRepository.update({ id: deployment.id }, { current: false, healthy: false, routed: false })
this.consoleLoggerService.log('FINISH:CREATE_UNDEPLOYMENT_EXECUTION', { execution: execution.id })
return execution
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,26 @@
* limitations under the License.
*/

import { Http, K8sManifest, Subset } from '../interfaces/k8s-manifest.interface'
import { Http, Subset } from '../interfaces/k8s-manifest.interface'
import { ISpinnakerConfigurationData } from '../../../api/configurations/interfaces'
import { Component, Deployment } from '../../../api/deployments/interfaces'
import { IstioManifestsUtils } from './istio-manifests.utilts'
import { DeploymentUtils } from './deployment.utils'
import { DeploymentComponent } from '../../../api/deployments/interfaces/deployment.interface'
import { DestinationRuleSpec, VirtualServiceSpec } from '../../../operator/params.interface'

const IstioDeploymentManifestsUtils = {

getVirtualServiceManifest: (deployment: Deployment, component: DeploymentComponent, activeByName: Component[]): K8sManifest => {
getVirtualServiceManifest: (deployment: Deployment, component: DeploymentComponent, activeByName: Component[]): VirtualServiceSpec => {
return {
apiVersion: 'networking.istio.io/v1beta1',
kind: 'VirtualService',
metadata: {
name: `${component.name}`,
namespace: `${(deployment.cdConfiguration.configurationData as ISpinnakerConfigurationData).namespace}`
namespace: `${(deployment.cdConfiguration.configurationData as ISpinnakerConfigurationData).namespace}`,
annotations: {
circles: JSON.stringify(activeByName.map(c => c.deployment.circleId))
}
},
spec: {
gateways: component.gatewayName ? [component.gatewayName] : [],
Expand All @@ -41,13 +45,16 @@ const IstioDeploymentManifestsUtils = {
}
},

getDestinationRulesManifest: (deployment: Deployment, component: DeploymentComponent, activeByName: Component[]): K8sManifest => {
getDestinationRulesManifest: (deployment: Deployment, component: DeploymentComponent, activeByName: Component[]): DestinationRuleSpec => {
return {
apiVersion: 'networking.istio.io/v1beta1',
kind: 'DestinationRule',
metadata: {
name: component.name,
namespace: `${(deployment.cdConfiguration.configurationData as ISpinnakerConfigurationData).namespace}`
namespace: `${(deployment.cdConfiguration.configurationData as ISpinnakerConfigurationData).namespace}`,
annotations: {
circles: JSON.stringify(activeByName.map(c => c.deployment.circleId))
}
},
spec: {
host: component.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,26 @@
* limitations under the License.
*/

import { Http, K8sManifest, Subset } from '../interfaces/k8s-manifest.interface'
import { Component, Deployment } from '../../../api/deployments/interfaces'
import { ISpinnakerConfigurationData } from '../../../api/configurations/interfaces'
import { IstioManifestsUtils } from './istio-manifests.utilts'
import { DeploymentUtils } from './deployment.utils'
import { Component, Deployment } from '../../../api/deployments/interfaces'
import { DeploymentComponent } from '../../../api/deployments/interfaces/deployment.interface'
import { DestinationRuleSpec, VirtualServiceSpec } from '../../../operator/params.interface'
import { Http, Subset } from '../interfaces/k8s-manifest.interface'
import { DeploymentUtils } from './deployment.utils'
import { IstioManifestsUtils } from './istio-manifests.utilts'

const IstioUndeploymentManifestsUtils = {

getVirtualServiceManifest: (deployment: Deployment, component: DeploymentComponent, activeByName: Component[]): K8sManifest => {
getVirtualServiceManifest: (deployment: Deployment, component: DeploymentComponent, activeByName: Component[]): VirtualServiceSpec => {
return {
apiVersion: 'networking.istio.io/v1beta1',
kind: 'VirtualService',
metadata: {
name: `${component.name}`,
namespace: `${(deployment.cdConfiguration.configurationData as ISpinnakerConfigurationData).namespace}`
namespace: `${(deployment.cdConfiguration.configurationData as ISpinnakerConfigurationData).namespace}`,
annotations: {
circles: JSON.stringify(activeByName.map(c => c.deployment.circleId))
}
},
spec: {
gateways: component.gatewayName ? [component.gatewayName] : [],
Expand All @@ -39,13 +43,17 @@ const IstioUndeploymentManifestsUtils = {
}
},

getEmptyVirtualServiceManifest: (deployment: Deployment, component: DeploymentComponent): K8sManifest => {
getEmptyVirtualServiceManifest: (deployment: Deployment, component: DeploymentComponent): VirtualServiceSpec => {
return {
apiVersion: 'networking.istio.io/v1beta1',
kind: 'VirtualService',
metadata: {
name: component.name,
namespace: `${(deployment.cdConfiguration.configurationData as ISpinnakerConfigurationData).namespace}`
namespace: `${(deployment.cdConfiguration.configurationData as ISpinnakerConfigurationData).namespace}`,
annotations: {
circles: JSON.stringify([])
}

},
spec: {
gateways: component.gatewayName ? [component.gatewayName] : [],
Expand Down Expand Up @@ -74,17 +82,21 @@ const IstioUndeploymentManifestsUtils = {
}
},

getDestinationRulesManifest: (deployment: Deployment, component: DeploymentComponent, activeByName: Component[]): K8sManifest => {
getDestinationRulesManifest: (deployment: Deployment, component: DeploymentComponent, activeByName: Component[]): DestinationRuleSpec => {
const istioSubsets = IstioUndeploymentManifestsUtils.getActiveComponentsSubsets(deployment.circleId, activeByName)
return {
apiVersion: 'networking.istio.io/v1beta1',
kind: 'DestinationRule',
metadata: {
name: component.name,
namespace: `${(deployment.cdConfiguration.configurationData as ISpinnakerConfigurationData).namespace}`
namespace: `${(deployment.cdConfiguration.configurationData as ISpinnakerConfigurationData).namespace}`,
annotations: {
circles: JSON.stringify(istioSubsets.map(s => s.labels.circleId))
}
},
spec: {
host: component.name,
subsets: IstioUndeploymentManifestsUtils.getActiveComponentsSubsets(deployment.circleId, activeByName)
subsets: istioSubsets
}
}
},
Expand Down
14 changes: 7 additions & 7 deletions butler/src/app/v2/operator/deployments.hook.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { KubernetesManifest } from '../core/integrations/interfaces/k8s-manifest
import { K8sClient } from '../core/integrations/k8s/client'
import { ConsoleLoggerService } from '../core/logs/console/console-logger.service'
import { HookParams } from './params.interface'
import { Reconcile } from './reconcile'
import { ReconcileDeployment } from './use-cases/reconcile-deployments.usecase'

@Controller('/')
export class DeploymentsHookController {
Expand All @@ -17,25 +17,25 @@ export class DeploymentsHookController {
private readonly deploymentRepository: DeploymentRepositoryV2,
private readonly componentRepository: ComponentsRepositoryV2,
private readonly configurationRepository: CdConfigurationsRepository,
private readonly consoleLoggerService: ConsoleLoggerService
private readonly consoleLoggerService: ConsoleLoggerService,
private readonly reconcileUseCase: ReconcileDeployment
) { }

@Post('/v2/operator/deployment/hook/reconcile')
@HttpCode(200)
@UsePipes(new ValidationPipe({ transform: true }))
public async reconcile(@Body() params: HookParams) : Promise<{status?: unknown, children: KubernetesManifest[], resyncAfterSeconds?: number}> {
const reconcile = new Reconcile()
const deployment = await this.deploymentRepository.findWithComponentsAndConfig(params.parent.spec.deploymentId)
const decryptedConfig = await this.configurationRepository.findDecrypted(deployment.cdConfiguration.id)
const rawSpecs = deployment.components.flatMap(c => c.manifests)
const specs = reconcile.addMetadata(rawSpecs, deployment)
const specs = this.reconcileUseCase.addMetadata(rawSpecs, deployment)

if (isEmpty(params.children['Deployment.apps/v1'])) {
return { children: specs, resyncAfterSeconds: 5 }
}
const currentDeploymentSpecs = reconcile.specsByDeployment(params, deployment.id)
const currentDeploymentSpecs = this.reconcileUseCase.specsByDeployment(params, deployment.id)

const allReady = reconcile.checkConditions(currentDeploymentSpecs)
const allReady = this.reconcileUseCase.checkConditions(currentDeploymentSpecs)
if (allReady === false) {
const previousDeploymentId = deployment.previousDeploymentId

Expand All @@ -44,7 +44,7 @@ export class DeploymentsHookController {
return { children: specs, resyncAfterSeconds: 5 }
}
const previousDeployment = await this.deploymentRepository.findWithComponentsAndConfig(previousDeploymentId)
const currentAndPrevious = reconcile.concatWithPrevious(previousDeployment, specs)
const currentAndPrevious = this.reconcileUseCase.concatWithPrevious(previousDeployment, specs)
return { children: currentAndPrevious, resyncAfterSeconds: 5 }
}

Expand Down
70 changes: 30 additions & 40 deletions butler/src/app/v2/operator/params.interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Http } from '../core/integrations/interfaces/k8s-manifest.interface'

export interface RouteHookParams {
controller?: Record<string, unknown>
parent: {
Expand All @@ -20,8 +22,8 @@ export interface RouteHookParams {
}

export interface RouteChildren {
'VirtualService.networking.istio.io/v1beta1': VirtualServiceSpec,
'DestinationRule.networking.istio.io/v1beta1': DestinationRuleSpec
'VirtualService.networking.istio.io/v1beta1': ChildVirtualServiceSpec,
'DestinationRule.networking.istio.io/v1beta1': ChildDestinationRuleSpec
}

export interface HookParams {
Expand Down Expand Up @@ -87,53 +89,42 @@ export interface DeploymentSpec {
}
}

export interface ChildVirtualServiceSpec {
[key: string]: VirtualServiceSpec
}

export interface ChildDestinationRuleSpec {
[key: string]: DestinationRuleSpec
}

export interface VirtualServiceSpec {
[key: string]: {
apiVersion: string
kind: string
metadata?: SpecMetadata
kind: 'VirtualService'
metadata: {
name: string
namespace: string
annotations: {
circles: string,
}
}
spec: {
gateways: string[]
hosts: string[]
http: {
match?: {
headers: {
cookie?: {
regex: string
}
'x-circle-id'?: {
exact: string
}
}
}[],
route: {
destination: {
host: string
subset: string
}
headers: {
request: {
set: {
'x-circle-source': string
}
},
response: {
set: {
'x-circle-source': string
}
}
}
}[]
}[]
http: Http[]
}
}
}

export interface DestinationRuleSpec {
[key: string]: {
apiVersion: string
kind: string
metadata?: SpecMetadata
kind: 'DestinationRule'
metadata: {
name: string
namespace: string
annotations: {
circles: string,
}
teste?: string
}
spec: {
host: string
subsets: {
Expand All @@ -146,7 +137,6 @@ export interface DestinationRuleSpec {
}[]
}
}
}

export interface ServiceSpec {
[key: string]: {
Expand Down
36 changes: 36 additions & 0 deletions butler/src/app/v2/operator/partial-params.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { VirtualServiceSpec, DestinationRuleSpec } from './params.interface'

type PartialVirtualServiceSpec = Pick<VirtualServiceSpec, 'kind' | 'metadata'>
type PartialDestinationRuleSpec = Pick<DestinationRuleSpec, 'kind' | 'metadata'>
export type SpecsUnion = PartialVirtualServiceSpec | PartialDestinationRuleSpec


export interface PartialRouteHookParams {
parent: {
spec: {
circles: {
components: {
name: string
tag: string
}[]
default: boolean
id: string
}[]
}
}
children: PartialRouteChildren
}

interface PartialRouteChildren {
'VirtualService.networking.istio.io/v1beta1': PartialChildVirtualServiceSpec,
'DestinationRule.networking.istio.io/v1beta1': PartialChildDestinationRuleSpec
}


interface PartialChildVirtualServiceSpec {
[key: string]: PartialVirtualServiceSpec
}

interface PartialChildDestinationRuleSpec {
[key: string]: PartialDestinationRuleSpec
}
Loading