-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Major refactor + introduction of duration policies (#12)
* Added duraton policies + refactor of POD handling * Abstracted StartupCPUBoost * Added tests to POD handler * Introduced documentation workflow
Showing
41 changed files
with
2,728 additions
and
618 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Copyright 2023 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
name: Documentation checks | ||
on: | ||
push: | ||
branches: | ||
- main | ||
pull_request: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
linting: | ||
name: Documentation linting | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v4 | ||
- name: Setup Python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: 3.9 | ||
- name: Check license headers | ||
run: python3 scripts/license_header_check.py $GITHUB_WORKSPACE | ||
- name: Markdown linter | ||
uses: avto-dev/markdown-lint@v1 | ||
with: | ||
config: '.markdownlint.yml' | ||
args: '**/*.md' | ||
- name: Markdown links | ||
uses: gaurav-nelson/github-action-markdown-link-check@v1 | ||
with: | ||
config-file: '.mlc_config.json' | ||
base-branch: main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Copyright 2023 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
MD013: | ||
line_length: 100 | ||
tables: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"ignorePatterns": [ | ||
{ | ||
"pattern": "^http://example.net" | ||
}, | ||
{ | ||
"pattern": "^https://docs.github.com" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package boost_test | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
autoscaling "github.com/google/kube-startup-cpu-boost/api/v1alpha1" | ||
bpod "github.com/google/kube-startup-cpu-boost/internal/boost/pod" | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
corev1 "k8s.io/api/core/v1" | ||
apiResource "k8s.io/apimachinery/pkg/api/resource" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
func TestBoost(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "Boost Suite") | ||
} | ||
|
||
var ( | ||
podTemplate *corev1.Pod | ||
annotTemplate *bpod.BoostPodAnnotation | ||
specTemplate *autoscaling.StartupCPUBoost | ||
) | ||
|
||
var _ = BeforeSuite(func() { | ||
specTemplate = &autoscaling.StartupCPUBoost{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "boost-001", | ||
Namespace: "demo", | ||
}, | ||
Spec: autoscaling.StartupCPUBoostSpec{ | ||
BoostPercent: 55, | ||
}, | ||
} | ||
annotTemplate = &bpod.BoostPodAnnotation{ | ||
BoostTimestamp: time.Now(), | ||
InitCPURequests: map[string]string{ | ||
"container-one": "500m", | ||
"continer-two": "500m", | ||
}, | ||
InitCPULimits: map[string]string{ | ||
"container-one": "1", | ||
"continer-two": "1", | ||
}, | ||
} | ||
reqQuantity, err := apiResource.ParseQuantity("1") | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
limitQuantity, err := apiResource.ParseQuantity("2") | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
podTemplate = &corev1.Pod{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "test-pod", | ||
Namespace: specTemplate.Namespace, | ||
Labels: map[string]string{ | ||
bpod.BoostLabelKey: specTemplate.Name, | ||
}, | ||
Annotations: map[string]string{ | ||
bpod.BoostAnnotationKey: annotTemplate.ToJSON(), | ||
}, | ||
}, | ||
Spec: corev1.PodSpec{ | ||
Containers: []corev1.Container{ | ||
{ | ||
Name: "container-one", | ||
Resources: corev1.ResourceRequirements{ | ||
Requests: corev1.ResourceList{ | ||
corev1.ResourceCPU: reqQuantity, | ||
}, | ||
Limits: corev1.ResourceList{ | ||
corev1.ResourceCPU: limitQuantity, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Name: "container-two", | ||
Resources: corev1.ResourceRequirements{ | ||
Requests: corev1.ResourceList{ | ||
corev1.ResourceCPU: reqQuantity, | ||
}, | ||
Limits: corev1.ResourceList{ | ||
corev1.ResourceCPU: limitQuantity, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package boost contains logic for managing startup resource boosts | ||
package boost |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package pod contains implementation of startup-cpu-boost POD manipulation | ||
// functions | ||
package pod | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"time" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
apiResource "k8s.io/apimachinery/pkg/api/resource" | ||
) | ||
|
||
const ( | ||
BoostLabelKey = "autoscaling.x-k8s.io/startup-cpu-boost" | ||
BoostAnnotationKey = "autoscaling.x-k8s.io/startup-cpu-boost" | ||
) | ||
|
||
type BoostPodAnnotation struct { | ||
BoostTimestamp time.Time `json:"timestamp,omitempty"` | ||
InitCPURequests map[string]string `json:"initCPURequests,omitempty"` | ||
InitCPULimits map[string]string `json:"initCPULimits,omitempty"` | ||
} | ||
|
||
func NewBoostAnnotation() *BoostPodAnnotation { | ||
return &BoostPodAnnotation{ | ||
BoostTimestamp: time.Now(), | ||
InitCPURequests: make(map[string]string), | ||
InitCPULimits: make(map[string]string), | ||
} | ||
} | ||
|
||
func (a BoostPodAnnotation) ToJSON() string { | ||
result, err := json.Marshal(a) | ||
if err != nil { | ||
panic("failed to marshall to JSON: " + err.Error()) | ||
} | ||
return string(result) | ||
} | ||
|
||
func BoostAnnotationFromPod(pod *corev1.Pod) (*BoostPodAnnotation, error) { | ||
annotation := &BoostPodAnnotation{} | ||
data, ok := pod.Annotations[BoostAnnotationKey] | ||
if !ok { | ||
return nil, errors.New("boost annotation not found") | ||
} | ||
if err := json.Unmarshal([]byte(data), annotation); err != nil { | ||
return nil, err | ||
} | ||
return annotation, nil | ||
} | ||
|
||
func RevertResourceBoost(pod *corev1.Pod) error { | ||
annotation, err := BoostAnnotationFromPod(pod) | ||
if err != nil { | ||
return fmt.Errorf("failed to get boost annotation from pod: %s", err) | ||
} | ||
delete(pod.Labels, BoostLabelKey) | ||
delete(pod.Annotations, BoostAnnotationKey) | ||
for _, container := range pod.Spec.Containers { | ||
if request, ok := annotation.InitCPURequests[container.Name]; ok { | ||
if reqQuantity, err := apiResource.ParseQuantity(request); err == nil { | ||
container.Resources.Requests[corev1.ResourceCPU] = reqQuantity | ||
} else { | ||
return fmt.Errorf("failed to parse CPU request: %s", err) | ||
} | ||
} | ||
if limit, ok := annotation.InitCPULimits[container.Name]; ok { | ||
if limitQuantity, err := apiResource.ParseQuantity(limit); err == nil { | ||
container.Resources.Limits[corev1.ResourceCPU] = limitQuantity | ||
} else { | ||
return fmt.Errorf("failed to parse CPU limit: %s", err) | ||
} | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package pod_test | ||
|
||
import ( | ||
"testing" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
func TestPod(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "Pod Suite") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package pod_test | ||
|
||
import ( | ||
"time" | ||
|
||
bpod "github.com/google/kube-startup-cpu-boost/internal/boost/pod" | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
corev1 "k8s.io/api/core/v1" | ||
apiResource "k8s.io/apimachinery/pkg/api/resource" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
var _ = Describe("Pod", func() { | ||
var pod *corev1.Pod | ||
var annot *bpod.BoostPodAnnotation | ||
var containerOne, containerTwo string | ||
var reqQuantity, limitQuantity apiResource.Quantity | ||
var err error | ||
|
||
BeforeEach(func() { | ||
containerOne = "container-one" | ||
containerTwo = "container-one" | ||
reqQuantity, err = apiResource.ParseQuantity("1") | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
limitQuantity, err = apiResource.ParseQuantity("2") | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
annot = &bpod.BoostPodAnnotation{ | ||
BoostTimestamp: time.Now(), | ||
InitCPURequests: map[string]string{ | ||
containerOne: "500m", | ||
containerTwo: "500m", | ||
}, | ||
InitCPULimits: map[string]string{ | ||
containerOne: "1", | ||
containerTwo: "1", | ||
}, | ||
} | ||
pod = &corev1.Pod{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "test-pod", | ||
Labels: map[string]string{ | ||
bpod.BoostLabelKey: "boost-001", | ||
}, | ||
Annotations: map[string]string{ | ||
bpod.BoostAnnotationKey: annot.ToJSON(), | ||
}, | ||
}, | ||
Spec: corev1.PodSpec{ | ||
Containers: []corev1.Container{ | ||
{ | ||
Name: containerOne, | ||
Resources: corev1.ResourceRequirements{ | ||
Requests: corev1.ResourceList{ | ||
corev1.ResourceCPU: reqQuantity, | ||
}, | ||
Limits: corev1.ResourceList{ | ||
corev1.ResourceCPU: limitQuantity, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Name: containerTwo, | ||
Resources: corev1.ResourceRequirements{ | ||
Requests: corev1.ResourceList{ | ||
corev1.ResourceCPU: reqQuantity, | ||
}, | ||
Limits: corev1.ResourceList{ | ||
corev1.ResourceCPU: limitQuantity, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
}) | ||
|
||
Describe("Reverts the POD container resources to original values", func() { | ||
When("POD is missing startup-cpu-boost annotation", func() { | ||
BeforeEach(func() { | ||
delete(pod.ObjectMeta.Annotations, bpod.BoostAnnotationKey) | ||
err = bpod.RevertResourceBoost(pod) | ||
}) | ||
It("errors", func() { | ||
Expect(err).Should(HaveOccurred()) | ||
}) | ||
}) | ||
When("POD has valid startup-cpu-boost annotation", func() { | ||
BeforeEach(func() { | ||
err = bpod.RevertResourceBoost(pod) | ||
}) | ||
It("does not error", func() { | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
}) | ||
It("removes startup-cpu-boost label", func() { | ||
Expect(pod.Labels).NotTo(HaveKey(bpod.BoostLabelKey)) | ||
}) | ||
It("removes startup-cpu-boost annotation", func() { | ||
Expect(pod.Annotations).NotTo(HaveKey(bpod.BoostAnnotationKey)) | ||
}) | ||
It("reverts CPU requests to initial values", func() { | ||
cpuReqOne := pod.Spec.Containers[0].Resources.Requests[corev1.ResourceCPU] | ||
cpuReqTwo := pod.Spec.Containers[1].Resources.Requests[corev1.ResourceCPU] | ||
Expect(cpuReqOne.String()).Should(Equal(annot.InitCPURequests[containerOne])) | ||
Expect(cpuReqTwo.String()).Should(Equal(annot.InitCPURequests[containerTwo])) | ||
}) | ||
It("reverts CPU limits to initial values", func() { | ||
cpuReqOne := pod.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU] | ||
cpuReqTwo := pod.Spec.Containers[1].Resources.Limits[corev1.ResourceCPU] | ||
Expect(cpuReqOne.String()).Should(Equal(annot.InitCPULimits[containerOne])) | ||
Expect(cpuReqTwo.String()).Should(Equal(annot.InitCPULimits[containerTwo])) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package policy | ||
|
||
import ( | ||
"time" | ||
|
||
v1 "k8s.io/api/core/v1" | ||
) | ||
|
||
const ( | ||
FixedDurationPolicyName = "FixedDuration" | ||
) | ||
|
||
type TimeFunc func() time.Time | ||
|
||
type FixedDurationPolicy struct { | ||
timeFunc TimeFunc | ||
duration time.Duration | ||
} | ||
|
||
func NewFixedDurationPolicy(duration time.Duration) DurationPolicy { | ||
return NewFixedDurationPolicyWithTimeFunc(time.Now, duration) | ||
} | ||
|
||
func NewFixedDurationPolicyWithTimeFunc(timeFunc TimeFunc, duration time.Duration) DurationPolicy { | ||
return &FixedDurationPolicy{ | ||
timeFunc: timeFunc, | ||
duration: duration, | ||
} | ||
} | ||
|
||
func (*FixedDurationPolicy) Name() string { | ||
return FixedDurationPolicyName | ||
} | ||
|
||
func (p *FixedDurationPolicy) Duration() time.Duration { | ||
return p.duration | ||
} | ||
|
||
func (p *FixedDurationPolicy) Valid(pod *v1.Pod) bool { | ||
now := p.timeFunc() | ||
return pod.CreationTimestamp.Add(p.duration).After(now) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package policy_test | ||
|
||
import ( | ||
"time" | ||
|
||
bpolicy "github.com/google/kube-startup-cpu-boost/internal/boost/policy" | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
var _ = Describe("FixedDurationPolicy", func() { | ||
var policy bpolicy.DurationPolicy | ||
var now time.Time | ||
var duration time.Duration | ||
var timeFunc bpolicy.TimeFunc | ||
|
||
BeforeEach(func() { | ||
now = time.Now() | ||
duration = 5 * time.Second | ||
timeFunc = func() time.Time { | ||
return now | ||
} | ||
policy = bpolicy.NewFixedDurationPolicyWithTimeFunc(timeFunc, duration) | ||
}) | ||
|
||
Describe("Validates POD", func() { | ||
When("the life time of a POD exceeds the policy duration", func() { | ||
It("returns policy is not valid", func() { | ||
creationTimesamp := now.Add(-1 * duration).Add(-1 * time.Minute) | ||
pod.CreationTimestamp = metav1.NewTime(creationTimesamp) | ||
Expect(policy.Valid(pod)).To(BeFalse()) | ||
}) | ||
}) | ||
When("the life time of a POD is within policy duration", func() { | ||
It("returns policy is valid", func() { | ||
creationTimesamp := now.Add(-1 * duration).Add(1 * time.Minute) | ||
pod.CreationTimestamp = metav1.NewTime(creationTimesamp) | ||
Expect(policy.Valid(pod)).To(BeTrue()) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package policy | ||
|
||
import ( | ||
corev1 "k8s.io/api/core/v1" | ||
) | ||
|
||
const ( | ||
PodConditionPolicyName = "PodCondition" | ||
) | ||
|
||
type PodConditionPolicy struct { | ||
condition corev1.PodConditionType | ||
status corev1.ConditionStatus | ||
} | ||
|
||
func NewPodConditionPolicy(condition corev1.PodConditionType, status corev1.ConditionStatus) DurationPolicy { | ||
return &PodConditionPolicy{ | ||
condition: condition, | ||
status: status, | ||
} | ||
} | ||
|
||
func (*PodConditionPolicy) Name() string { | ||
return PodConditionPolicyName | ||
} | ||
|
||
func (p *PodConditionPolicy) Condition() corev1.PodConditionType { | ||
return p.condition | ||
} | ||
|
||
func (p *PodConditionPolicy) Status() corev1.ConditionStatus { | ||
return p.status | ||
} | ||
|
||
func (p *PodConditionPolicy) Valid(pod *corev1.Pod) bool { | ||
for _, condition := range pod.Status.Conditions { | ||
if condition.Type != p.condition { | ||
continue | ||
} | ||
if condition.Status == p.status { | ||
return false | ||
} | ||
} | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package policy_test | ||
|
||
import ( | ||
bpolicy "github.com/google/kube-startup-cpu-boost/internal/boost/policy" | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
corev1 "k8s.io/api/core/v1" | ||
) | ||
|
||
var _ = Describe("PodConditionPolicy", func() { | ||
var policy bpolicy.DurationPolicy | ||
var condition corev1.PodConditionType | ||
var status corev1.ConditionStatus | ||
|
||
BeforeEach(func() { | ||
condition = corev1.PodReady | ||
status = corev1.ConditionTrue | ||
policy = bpolicy.NewPodConditionPolicy(condition, status) | ||
}) | ||
|
||
Describe("Validates POD", func() { | ||
Context("when the POD has no condition with matching type", func() { | ||
BeforeEach(func() { | ||
pod.Status.Conditions = make([]corev1.PodCondition, 0) | ||
}) | ||
It("returns policy is valid", func() { | ||
Expect(policy.Valid(pod)).To(BeTrue()) | ||
}) | ||
}) | ||
Context("when the POD has condition with matching type", func() { | ||
BeforeEach(func() { | ||
pod.Status.Conditions = []corev1.PodCondition{ | ||
{ | ||
Type: condition, | ||
Status: corev1.ConditionUnknown, | ||
}, | ||
} | ||
}) | ||
When("condition status matches status in a policy", func() { | ||
It("returns policy is invalid", func() { | ||
pod.Status.Conditions[0].Status = status | ||
Expect(policy.Valid(pod)).To(BeFalse()) | ||
}) | ||
}) | ||
When("condition status does not match status in a policy", func() { | ||
It("returns policy is valid", func() { | ||
Expect(policy.Valid(pod)).To(BeTrue()) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package policy contains implementation of resource boost duration policies | ||
package policy | ||
|
||
import corev1 "k8s.io/api/core/v1" | ||
|
||
const ( | ||
PolicyTypeFixed = "Fixed" | ||
PolicyTypePodCondition = "PodCondition" | ||
) | ||
|
||
type DurationPolicy interface { | ||
Valid(pod *corev1.Pod) bool | ||
Name() string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package policy_test | ||
|
||
import ( | ||
"testing" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
var pod *corev1.Pod | ||
|
||
func TestPolicy(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "Policy Suite") | ||
} | ||
|
||
var _ = BeforeSuite(func() { | ||
pod = &corev1.Pod{ | ||
ObjectMeta: metav1.ObjectMeta{}, | ||
Status: corev1.PodStatus{ | ||
Conditions: []corev1.PodCondition{}, | ||
}, | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package controller_test | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
autoscaling "github.com/google/kube-startup-cpu-boost/api/v1alpha1" | ||
bpod "github.com/google/kube-startup-cpu-boost/internal/boost/pod" | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
corev1 "k8s.io/api/core/v1" | ||
apiResource "k8s.io/apimachinery/pkg/api/resource" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
var ( | ||
podTemplate *corev1.Pod | ||
annotTemplate *bpod.BoostPodAnnotation | ||
specTemplate *autoscaling.StartupCPUBoost | ||
) | ||
|
||
func TestController(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "Controller Suite") | ||
} | ||
|
||
var _ = BeforeSuite(func() { | ||
specTemplate = &autoscaling.StartupCPUBoost{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "boost-001", | ||
Namespace: "demo", | ||
}, | ||
Spec: autoscaling.StartupCPUBoostSpec{ | ||
BoostPercent: 55, | ||
}, | ||
} | ||
annotTemplate = &bpod.BoostPodAnnotation{ | ||
BoostTimestamp: time.Now(), | ||
InitCPURequests: map[string]string{ | ||
"container-one": "500m", | ||
"continer-two": "500m", | ||
}, | ||
InitCPULimits: map[string]string{ | ||
"container-one": "1", | ||
"continer-two": "1", | ||
}, | ||
} | ||
reqQuantity, err := apiResource.ParseQuantity("1") | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
limitQuantity, err := apiResource.ParseQuantity("2") | ||
Expect(err).ShouldNot(HaveOccurred()) | ||
podTemplate = &corev1.Pod{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "test-pod", | ||
Namespace: specTemplate.Namespace, | ||
Labels: map[string]string{ | ||
bpod.BoostLabelKey: specTemplate.Name, | ||
}, | ||
Annotations: map[string]string{ | ||
bpod.BoostAnnotationKey: annotTemplate.ToJSON(), | ||
}, | ||
}, | ||
Spec: corev1.PodSpec{ | ||
Containers: []corev1.Container{ | ||
{ | ||
Name: "container-one", | ||
Resources: corev1.ResourceRequirements{ | ||
Requests: corev1.ResourceList{ | ||
corev1.ResourceCPU: reqQuantity, | ||
}, | ||
Limits: corev1.ResourceList{ | ||
corev1.ResourceCPU: limitQuantity, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Name: "container-two", | ||
Resources: corev1.ResourceRequirements{ | ||
Requests: corev1.ResourceList{ | ||
corev1.ResourceCPU: reqQuantity, | ||
}, | ||
Limits: corev1.ResourceList{ | ||
corev1.ResourceCPU: limitQuantity, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package controller contains implementation of a controllers managed | ||
// by the controller-manager | ||
package controller |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package mock is a generated GoMock package. | ||
package mock |
Oops, something went wrong.
Oops, something went wrong.