Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bluegreen preview endpoint rollout #2

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/golang-ci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v3
with:
go-version: '1.17.7'
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.43.0
skip-go-installation: true

# Optional: working directory, useful for monorepos
# working-directory: somedir
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ gen-yaml:
kustomize build ./install/sample/overlays/rollout-bluegreen > ./out/yaml/sample-greeting-rollout-bluegreen.yaml
kustomize build ./install/sample/overlays/remote > ./out/yaml/remotecluster_sample.yaml
cp ./install/sample/sample_dep.yaml ./out/yaml/sample_dep.yaml
cp ./install/sample/greeting_preview.yaml ./out/yaml/greeting_preview.yaml
cp ./install/sample/gtp.yaml ./out/yaml/gtp.yaml
cp ./install/sample/gtp_failover.yaml ./out/yaml/gtp_failover.yaml
cp ./install/sample/gtp_topology.yaml ./out/yaml/gtp_topology.yaml
Expand Down
16 changes: 11 additions & 5 deletions admiral/pkg/clusters/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ func copyEndpoint(e *v1alpha32.ServiceEntry_Endpoint) *v1alpha32.ServiceEntry_En

// A rollout can use one of 2 stratergies :-
// 1. Canary strategy - which can use a virtual service to manage the weights associated with a stable and canary service. Admiral created endpoints in service entries will use the weights assigned in the Virtual Service
// 2. Blue green strategy- this contains 2 service instances in a namespace, an active service and a preview service. Admiral will always use the active service
// 2. Blue green strategy- this contains 2 service instances in a namespace, an active service and a preview service. Admiral will use repective service to create active and preview endpoints
func getServiceForRollout(rc *RemoteController, rollout *argo.Rollout) map[string]*WeightedService {

if rollout == nil {
Expand All @@ -769,9 +769,12 @@ func getServiceForRollout(rc *RemoteController, rollout *argo.Rollout) map[strin
var istioCanaryWeights = make(map[string]int32)

var blueGreenActiveService string
var blueGreenPreviewService string

if rolloutStrategy.BlueGreen != nil {
// If rollout uses blue green strategy, use the active service
// If rollout uses blue green strategy
blueGreenActiveService = rolloutStrategy.BlueGreen.ActiveService
blueGreenPreviewService = rolloutStrategy.BlueGreen.PreviewService
} else if rolloutStrategy.Canary != nil && rolloutStrategy.Canary.TrafficRouting != nil && rolloutStrategy.Canary.TrafficRouting.Istio != nil {
canaryService = rolloutStrategy.Canary.CanaryService
stableService = rolloutStrategy.Canary.StableService
Expand Down Expand Up @@ -844,7 +847,7 @@ func getServiceForRollout(rc *RemoteController, rollout *argo.Rollout) map[strin
var service = servicesInNamespace[s]
var match = true
//skip services that are not referenced in the rollout
if len(blueGreenActiveService) > 0 && service.ObjectMeta.Name != blueGreenActiveService {
if len(blueGreenActiveService) > 0 && service.ObjectMeta.Name != blueGreenActiveService && service.ObjectMeta.Name != blueGreenPreviewService {
log.Infof("Skipping service=%s for rollout=%s in namespace=%s and cluster=%s", service.Name, rollout.Name, rollout.Namespace, rc.ClusterID)
continue
}
Expand All @@ -865,8 +868,11 @@ func getServiceForRollout(rc *RemoteController, rollout *argo.Rollout) map[strin
if match {
ports := GetMeshPortsForRollout(rc.ClusterID, service, rollout)
if len(ports) > 0 {
//if the strategy is bluegreen or using canary with NO istio traffic management, pick the first service that matches
if len(istioCanaryWeights) == 0 {
// if the strategy is bluegreen return matched services
// else if using canary with NO istio traffic management, pick the first service that matches
if rolloutStrategy.BlueGreen != nil {
matchedServices[service.Name] = &WeightedService{Weight: 1, Service: service}
} else if len(istioCanaryWeights) == 0 {
matchedServices[service.Name] = &WeightedService{Weight: 1, Service: service}
break
}
Expand Down
58 changes: 53 additions & 5 deletions admiral/pkg/clusters/serviceentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func modifyServiceEntryForNewServiceOrPod(event admiral.EventType, env string, s
var serviceEntries = make(map[string]*networking.ServiceEntry)

var cname string
cnames := make(map[string]string)
var serviceInstance *k8sV1.Service
var weightedServices map[string]*WeightedService
var rollout *admiral.RolloutClusterEntry
Expand Down Expand Up @@ -104,6 +105,7 @@ func modifyServiceEntryForNewServiceOrPod(event admiral.EventType, env string, s
localMeshPorts := GetMeshPortsForRollout(rc.ClusterID, serviceInstance, rolloutInstance)

cname = common.GetCnameForRollout(rolloutInstance, common.GetWorkloadIdentifier(), common.GetHostnameSuffix())
cnames[cname] = "1"
sourceRollouts[rc.ClusterID] = rolloutInstance
createServiceEntryForRollout(event, rc, remoteRegistry.AdmiralCache, localMeshPorts, rolloutInstance, serviceEntries)
} else {
Expand Down Expand Up @@ -132,10 +134,14 @@ func modifyServiceEntryForNewServiceOrPod(event admiral.EventType, env string, s
localFqdn := serviceInstance.Name + common.Sep + serviceInstance.Namespace + common.DotLocalDomainSuffix
rc := remoteRegistry.RemoteControllers[sourceCluster]
var meshPorts map[string]uint32
isBlueGreenStrategy := false

if len(sourceRollouts) > 0 {
isBlueGreenStrategy = sourceRollouts[sourceCluster].Spec.Strategy.BlueGreen != nil
}

if len(sourceDeployments) > 0 {
meshPorts = GetMeshPorts(sourceCluster, serviceInstance, sourceDeployments[sourceCluster])
} else {
meshPorts = GetMeshPortsForRollout(sourceCluster, serviceInstance, sourceRollouts[sourceCluster])
}

for key, serviceEntry := range serviceEntries {
Expand All @@ -147,9 +153,20 @@ func modifyServiceEntryForNewServiceOrPod(event admiral.EventType, env string, s
for _, ep := range serviceEntry.Endpoints {
//replace istio ingress-gateway address with local fqdn, note that ingress-gateway can be empty (not provisoned, or is not up)
if ep.Address == clusterIngress || ep.Address == "" {
// see if we have weighted services (rollouts with canary strategy)
if len(weightedServices) > 1 {
// Update endpoints with locafqdn for active and preview se of bluegreen rollout
if isBlueGreenStrategy {
oldPorts := ep.Ports
updateEndpointsForBlueGreen(sourceRollouts[sourceCluster], weightedServices, cnames, ep, sourceCluster, key)

AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, map[string]string{sourceCluster: sourceCluster}, remoteRegistry.RemoteControllers,
map[string]*networking.ServiceEntry{key: serviceEntry})
//swap it back to use for next iteration
ep.Address = clusterIngress
ep.Ports = oldPorts
// see if we have weighted services (rollouts with canary strategy)
} else if len(weightedServices) > 1 {
//add one endpoint per each service, may be modify
meshPorts = GetMeshPortsForRollout(sourceCluster, serviceInstance, sourceRollouts[sourceCluster])
var se = copyServiceEntry(serviceEntry)
updateEndpointsForWeightedServices(se, weightedServices, clusterIngress, meshPorts)
AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, map[string]string{sourceCluster: sourceCluster}, remoteRegistry.RemoteControllers,
Expand All @@ -169,7 +186,7 @@ func modifyServiceEntryForNewServiceOrPod(event admiral.EventType, env string, s
}

for _, val := range dependents {
remoteRegistry.AdmiralCache.DependencyNamespaceCache.Put(val, serviceInstance.Namespace, localFqdn, map[string]string{cname: "1"})
remoteRegistry.AdmiralCache.DependencyNamespaceCache.Put(val, serviceInstance.Namespace, localFqdn, cnames)
}

if common.GetWorkloadSidecarUpdate() == "enabled" {
Expand All @@ -179,6 +196,26 @@ func modifyServiceEntryForNewServiceOrPod(event admiral.EventType, env string, s
return serviceEntries
}

func updateEndpointsForBlueGreen(rollout *argo.Rollout, weightedServices map[string]*WeightedService, cnames map[string]string,
ep *networking.ServiceEntry_Endpoint, sourceCluster string, meshHost string) {
activeServiceName := rollout.Spec.Strategy.BlueGreen.ActiveService
previewServiceName := rollout.Spec.Strategy.BlueGreen.PreviewService

if previewService, ok := weightedServices[previewServiceName]; strings.HasPrefix(meshHost, common.BlueGreenRolloutPreviewPrefix+common.Sep) && ok {
previewServiceInstance := previewService.Service
localFqdn := previewServiceInstance.Name + common.Sep + previewServiceInstance.Namespace + common.DotLocalDomainSuffix
cnames[localFqdn] = "1"
ep.Address = localFqdn
ep.Ports = GetMeshPortsForRollout(sourceCluster, previewServiceInstance, rollout)
} else if activeService, ok := weightedServices[activeServiceName]; ok {
activeServiceInstance := activeService.Service
localFqdn := activeServiceInstance.Name + common.Sep + activeServiceInstance.Namespace + common.DotLocalDomainSuffix
cnames[localFqdn] = "1"
ep.Address = localFqdn
ep.Ports = GetMeshPortsForRollout(sourceCluster, activeServiceInstance, rollout)
}
}

//update endpoints for Argo rollouts specific Service Entries to account for traffic splitting (Canary strategy)
func updateEndpointsForWeightedServices(serviceEntry *networking.ServiceEntry, weightedServices map[string]*WeightedService, clusterIngress string, meshPorts map[string]uint32) {
var endpoints = make([]*networking.ServiceEntry_Endpoint, 0)
Expand Down Expand Up @@ -570,6 +607,17 @@ func createServiceEntryForRollout(event admiral.EventType, rc *RemoteController,

san := getSanForRollout(destRollout, workloadIdentityKey)

if destRollout.Spec.Strategy.BlueGreen != nil && destRollout.Spec.Strategy.BlueGreen.PreviewService != "" {
rolloutServices := getServiceForRollout(rc, destRollout)
if _, ok := rolloutServices[destRollout.Spec.Strategy.BlueGreen.PreviewService]; ok {
previewGlobalFqdn := common.BlueGreenRolloutPreviewPrefix + common.Sep + common.GetCnameForRollout(destRollout, workloadIdentityKey, common.GetHostnameSuffix())
previewAddress := getUniqueAddress(admiralCache, previewGlobalFqdn)
if len(previewGlobalFqdn) != 0 && len(previewAddress) != 0 {
generateServiceEntry(event, admiralCache, meshPorts, previewGlobalFqdn, rc, serviceEntries, previewAddress, san)
}
}
}

tmpSe := generateServiceEntry(event, admiralCache, meshPorts, globalFqdn, rc, serviceEntries, address, san)
return tmpSe
}
Expand Down
Loading