Commit 264a850a authored by Jason Goodman's avatar Jason Goodman Committed by Thong Kuah

Remove deploy_boards_dedupe_instances feature flag

Rollout bug fix to self managed instances
Update documentation and specs
parent 13e00158
---
name: deploy_boards_dedupe_instances
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40768
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258214
type: development
group: group::progressive delivery
default_enabled: false
......@@ -41,9 +41,9 @@ knowledge. In particular, you should be familiar with:
- [Kubernetes namespaces](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/)
- [Kubernetes canary deployments](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments)
NOTE: **Note:**
Apps that consist of multiple deployments are shown as duplicates on the deploy board.
Follow [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/8463) for details.
In GitLab 13.5 and earlier, apps that consist of multiple deployments are shown as
duplicates on the deploy board. This is [fixed](https://gitlab.com/gitlab-org/gitlab/-/issues/8463)
in GitLab 13.6.
## Use cases
......
---
title: Fix duplicate instances in deploy boards when multiple deployments have the same track
merge_request: 46374
author:
type: fixed
......@@ -54,13 +54,7 @@ module Gitlab
def initialize(deployments, pods: [], ingresses: [], status: :found)
@status = status
@deployments = deployments
@instances = if ::Feature.enabled?(:deploy_boards_dedupe_instances)
RolloutInstances.new(deployments, pods).pod_instances
else
deployments.flat_map(&:instances)
end
@instances = RolloutInstances.new(deployments, pods).pod_instances
@canary_ingress = ingresses.find(&:canary?)
@completion =
......
......@@ -30,257 +30,237 @@ RSpec.describe Gitlab::Kubernetes::RolloutStatus do
subject(:rollout_status) { described_class.from_deployments(*specs, pods_attrs: pods, ingresses: ingresses) }
shared_examples 'rollout status' do
describe '#deployments' do
it 'stores the deployments' do
expect(rollout_status.deployments).to be_kind_of(Array)
expect(rollout_status.deployments.size).to eq(2)
expect(rollout_status.deployments.first).to be_kind_of(::Gitlab::Kubernetes::Deployment)
end
describe '#deployments' do
it 'stores the deployments' do
expect(rollout_status.deployments).to be_kind_of(Array)
expect(rollout_status.deployments.size).to eq(2)
expect(rollout_status.deployments.first).to be_kind_of(::Gitlab::Kubernetes::Deployment)
end
end
describe '#instances' do
context 'for stable track' do
let(:track) { "any" }
describe '#instances' do
context 'for stable track' do
let(:track) { "any" }
let(:pods) do
create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: "any")
end
it 'stores the union of deployment instances' do
expected = [
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'any', stable: false },
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'any', stable: false },
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'any', stable: false },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true }
]
expect(rollout_status.instances).to eq(expected)
end
let(:pods) do
create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: "any")
end
context 'for stable track' do
let(:track) { 'canary' }
let(:pods) do
create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: track)
end
it 'sorts stable instances last' do
expected = [
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'canary', stable: false },
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'canary', stable: false },
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'canary', stable: false },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true }
]
expect(rollout_status.instances).to eq(expected)
end
it 'stores the union of deployment instances' do
expected = [
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'any', stable: false },
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'any', stable: false },
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'any', stable: false },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true }
]
expect(rollout_status.instances).to eq(expected)
end
end
describe '#completion' do
subject { rollout_status.completion }
context 'for stable track' do
let(:track) { 'canary' }
context 'when all instances are finished' do
let(:track) { 'canary' }
let(:pods) do
create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: track)
end
it { is_expected.to eq(100) }
it 'sorts stable instances last' do
expected = [
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'canary', stable: false },
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'canary', stable: false },
{ status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'canary', stable: false },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
{ status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true }
]
expect(rollout_status.instances).to eq(expected)
end
end
end
context 'when half of the instances are finished' do
let(:track) { "canary" }
describe '#completion' do
subject { rollout_status.completion }
let(:pods) do
create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: track, status: "Pending")
end
context 'when all instances are finished' do
let(:track) { 'canary' }
let(:specs) { specs_half_finished }
it { is_expected.to eq(100) }
end
it { is_expected.to eq(50) }
context 'when half of the instances are finished' do
let(:track) { "canary" }
let(:pods) do
create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: track, status: "Pending")
end
context 'with one deployment' do
it 'sets the completion percentage when a deployment has more running pods than desired' do
deployments = [kube_deployment(name: 'one', track: 'one', replicas: 2)]
pods = create_pods(name: 'one', track: 'one', count: 3)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
let(:specs) { specs_half_finished }
expect(rollout_status.completion).to eq(100)
end
end
it { is_expected.to eq(50) }
end
context 'with two deployments on different tracks' do
it 'sets the completion percentage when all pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'one', replicas: 2),
kube_deployment(name: 'two', track: 'two', replicas: 2)
]
pods = create_pods(name: 'one', track: 'one', count: 2) + create_pods(name: 'two', track: 'two', count: 2)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
expect(rollout_status.completion).to eq(100)
end
end
context 'with one deployment' do
it 'sets the completion percentage when a deployment has more running pods than desired' do
deployments = [kube_deployment(name: 'one', track: 'one', replicas: 2)]
pods = create_pods(name: 'one', track: 'one', count: 3)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
context 'with two deployments that both have track set to "stable"' do
it 'sets the completion percentage when all pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 2),
kube_deployment(name: 'two', track: 'stable', replicas: 2)
]
pods = create_pods(name: 'one', track: 'stable', count: 2) + create_pods(name: 'two', track: 'stable', count: 2)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
expect(rollout_status.completion).to eq(100)
end
it 'sets the completion percentage when no pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 3),
kube_deployment(name: 'two', track: 'stable', replicas: 7)
]
rollout_status = described_class.from_deployments(*deployments, pods_attrs: [])
expect(rollout_status.completion).to eq(0)
end
expect(rollout_status.completion).to eq(100)
end
end
context 'with two deployments on different tracks' do
it 'sets the completion percentage when all pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'one', replicas: 2),
kube_deployment(name: 'two', track: 'two', replicas: 2)
]
pods = create_pods(name: 'one', track: 'one', count: 2) + create_pods(name: 'two', track: 'two', count: 2)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
context 'with two deployments, one with track set to "stable" and one with no track label' do
it 'sets the completion percentage when all pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 3),
kube_deployment(name: 'two', track: nil, replicas: 3)
]
pods = create_pods(name: 'one', track: 'stable', count: 3) + create_pods(name: 'two', track: nil, count: 3)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
expect(rollout_status.completion).to eq(100)
end
it 'sets the completion percentage when no pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 1),
kube_deployment(name: 'two', track: nil, replicas: 1)
]
rollout_status = described_class.from_deployments(*deployments, pods_attrs: [])
expect(rollout_status.completion).to eq(0)
end
expect(rollout_status.completion).to eq(100)
end
end
describe '#complete?' do
subject { rollout_status.complete? }
context 'when all instances are finished' do
let(:track) { 'canary' }
context 'with two deployments that both have track set to "stable"' do
it 'sets the completion percentage when all pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 2),
kube_deployment(name: 'two', track: 'stable', replicas: 2)
]
pods = create_pods(name: 'one', track: 'stable', count: 2) + create_pods(name: 'two', track: 'stable', count: 2)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
it { is_expected.to be_truthy }
expect(rollout_status.completion).to eq(100)
end
context 'when half of the instances are finished' do
let(:track) { "canary" }
it 'sets the completion percentage when no pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 3),
kube_deployment(name: 'two', track: 'stable', replicas: 7)
]
rollout_status = described_class.from_deployments(*deployments, pods_attrs: [])
let(:pods) do
create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: track, status: "Pending")
end
expect(rollout_status.completion).to eq(0)
end
let(:specs) { specs_half_finished }
it 'sets the completion percentage when a quarter of the pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 6),
kube_deployment(name: 'two', track: 'stable', replicas: 2)
]
pods = create_pods(name: 'one', track: 'stable', count: 2)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
it { is_expected.to be_falsy}
expect(rollout_status.completion).to eq(25)
end
end
describe '#found?' do
context 'when the specs are passed' do
it { is_expected.to be_found }
context 'with two deployments, one with track set to "stable" and one with no track label' do
it 'sets the completion percentage when all pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 3),
kube_deployment(name: 'two', track: nil, replicas: 3)
]
pods = create_pods(name: 'one', track: 'stable', count: 3) + create_pods(name: 'two', track: nil, count: 3)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
expect(rollout_status.completion).to eq(100)
end
context 'when list of specs is empty' do
let(:specs) { [] }
it 'sets the completion percentage when no pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 1),
kube_deployment(name: 'two', track: nil, replicas: 1)
]
rollout_status = described_class.from_deployments(*deployments, pods_attrs: [])
it { is_expected.not_to be_found }
expect(rollout_status.completion).to eq(0)
end
end
describe '.loading' do
subject { described_class.loading }
it 'sets the completion percentage when a third of the pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 2),
kube_deployment(name: 'two', track: nil, replicas: 7)
]
pods = create_pods(name: 'one', track: 'stable', count: 2) + create_pods(name: 'two', track: nil, count: 1)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
it { is_expected.to be_loading }
expect(rollout_status.completion).to eq(33)
end
end
end
describe '#not_found?' do
context 'when the specs are passed' do
it { is_expected.not_to be_not_found }
end
describe '#complete?' do
subject { rollout_status.complete? }
context 'when list of specs is empty' do
let(:specs) { [] }
context 'when all instances are finished' do
let(:track) { 'canary' }
it { is_expected.to be_not_found }
end
it { is_expected.to be_truthy }
end
describe '#canary_ingress_exists?' do
context 'when canary ingress exists' do
let(:ingresses) { [kube_ingress(track: :canary)] }
context 'when half of the instances are finished' do
let(:track) { "canary" }
it 'returns true' do
expect(rollout_status.canary_ingress_exists?).to eq(true)
end
let(:pods) do
create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: track, status: "Pending")
end
context 'when canary ingress does not exist' do
let(:ingresses) { [kube_ingress(track: :stable)] }
let(:specs) { specs_half_finished }
it 'returns false' do
expect(rollout_status.canary_ingress_exists?).to eq(false)
end
end
it { is_expected.to be_falsy}
end
end
context 'deploy_boards_dedupe_instances is disabled' do
before do
stub_feature_flags(deploy_boards_dedupe_instances: false)
describe '#found?' do
context 'when the specs are passed' do
it { is_expected.to be_found }
end
it_behaves_like 'rollout status'
context 'when list of specs is empty' do
let(:specs) { [] }
it { is_expected.not_to be_found }
end
end
describe '.loading' do
subject { described_class.loading }
it { is_expected.to be_loading }
end
context 'deploy_boards_dedupe_instances is enabled' do
before do
stub_feature_flags(deploy_boards_dedupe_instances: true)
describe '#not_found?' do
context 'when the specs are passed' do
it { is_expected.not_to be_not_found }
end
it_behaves_like 'rollout status'
context 'when list of specs is empty' do
let(:specs) { [] }
describe '#completion' do
it 'sets the completion percentage when a quarter of the pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 6),
kube_deployment(name: 'two', track: 'stable', replicas: 2)
]
pods = create_pods(name: 'one', track: 'stable', count: 2)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
it { is_expected.to be_not_found }
end
end
expect(rollout_status.completion).to eq(25)
describe '#canary_ingress_exists?' do
context 'when canary ingress exists' do
let(:ingresses) { [kube_ingress(track: :canary)] }
it 'returns true' do
expect(rollout_status.canary_ingress_exists?).to eq(true)
end
end
it 'sets the completion percentage when a third of the pods are complete' do
deployments = [
kube_deployment(name: 'one', track: 'stable', replicas: 2),
kube_deployment(name: 'two', track: nil, replicas: 7)
]
pods = create_pods(name: 'one', track: 'stable', count: 2) + create_pods(name: 'two', track: nil, count: 1)
rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
context 'when canary ingress does not exist' do
let(:ingresses) { [kube_ingress(track: :stable)] }
expect(rollout_status.completion).to eq(33)
it 'returns false' do
expect(rollout_status.canary_ingress_exists?).to eq(false)
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment