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

Commit

Permalink
Merge pull request #912 from ZupIT/butler-operator-health-status
Browse files Browse the repository at this point in the history
Butler operator health status
  • Loading branch information
cpgo authored Feb 22, 2021
2 parents fecabab + b840652 commit 1fd4e7c
Show file tree
Hide file tree
Showing 18 changed files with 671 additions and 476 deletions.
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

0 comments on commit 1fd4e7c

Please sign in to comment.