Delete apis directory
This commit is contained in:
parent
ddc2918a48
commit
b61a89f0bd
|
|
@ -1,89 +0,0 @@
|
||||||
package appconfig
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AppConfig struct {
|
|
||||||
AppID string `json:"github_app_id"`
|
|
||||||
AppInstallationID int64 `json:"github_app_installation_id"`
|
|
||||||
AppPrivateKey string `json:"github_app_private_key"`
|
|
||||||
|
|
||||||
Token string `json:"github_token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *AppConfig) tidy() *AppConfig {
|
|
||||||
if len(c.Token) > 0 {
|
|
||||||
return &AppConfig{
|
|
||||||
Token: c.Token,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &AppConfig{
|
|
||||||
AppID: c.AppID,
|
|
||||||
AppInstallationID: c.AppInstallationID,
|
|
||||||
AppPrivateKey: c.AppPrivateKey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *AppConfig) Validate() error {
|
|
||||||
if c == nil {
|
|
||||||
return fmt.Errorf("missing app config")
|
|
||||||
}
|
|
||||||
hasToken := len(c.Token) > 0
|
|
||||||
hasGitHubAppAuth := c.hasGitHubAppAuth()
|
|
||||||
if hasToken && hasGitHubAppAuth {
|
|
||||||
return fmt.Errorf("both PAT and GitHub App credentials provided. should only provide one")
|
|
||||||
}
|
|
||||||
if !hasToken && !hasGitHubAppAuth {
|
|
||||||
return fmt.Errorf("no credentials provided: either a PAT or GitHub App credentials should be provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *AppConfig) hasGitHubAppAuth() bool {
|
|
||||||
return len(c.AppID) > 0 && c.AppInstallationID > 0 && len(c.AppPrivateKey) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func FromSecret(secret *corev1.Secret) (*AppConfig, error) {
|
|
||||||
var appInstallationID int64
|
|
||||||
if v := string(secret.Data["github_app_installation_id"]); v != "" {
|
|
||||||
val, err := strconv.ParseInt(v, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
appInstallationID = val
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := &AppConfig{
|
|
||||||
Token: string(secret.Data["github_token"]),
|
|
||||||
AppID: string(secret.Data["github_app_id"]),
|
|
||||||
AppInstallationID: appInstallationID,
|
|
||||||
AppPrivateKey: string(secret.Data["github_app_private_key"]),
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cfg.Validate(); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to validate config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg.tidy(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func FromJSONString(v string) (*AppConfig, error) {
|
|
||||||
var appConfig AppConfig
|
|
||||||
if err := json.NewDecoder(bytes.NewBufferString(v)).Decode(&appConfig); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := appConfig.Validate(); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to validate app config decoded from string: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return appConfig.tidy(), nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
package appconfig
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAppConfigValidate_invalid(t *testing.T) {
|
|
||||||
tt := map[string]*AppConfig{
|
|
||||||
"empty": {},
|
|
||||||
"token and app config": {
|
|
||||||
AppID: "1",
|
|
||||||
AppInstallationID: 2,
|
|
||||||
AppPrivateKey: "private key",
|
|
||||||
Token: "token",
|
|
||||||
},
|
|
||||||
"app id not set": {
|
|
||||||
AppInstallationID: 2,
|
|
||||||
AppPrivateKey: "private key",
|
|
||||||
},
|
|
||||||
"app installation id not set": {
|
|
||||||
AppID: "2",
|
|
||||||
AppPrivateKey: "private key",
|
|
||||||
},
|
|
||||||
"private key empty": {
|
|
||||||
AppID: "2",
|
|
||||||
AppInstallationID: 1,
|
|
||||||
AppPrivateKey: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, cfg := range tt {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
err := cfg.Validate()
|
|
||||||
require.Error(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppConfigValidate_valid(t *testing.T) {
|
|
||||||
tt := map[string]*AppConfig{
|
|
||||||
"token": {
|
|
||||||
Token: "token",
|
|
||||||
},
|
|
||||||
"app ID": {
|
|
||||||
AppID: "1",
|
|
||||||
AppInstallationID: 2,
|
|
||||||
AppPrivateKey: "private key",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, cfg := range tt {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
err := cfg.Validate()
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppConfigFromSecret_invalid(t *testing.T) {
|
|
||||||
tt := map[string]map[string]string{
|
|
||||||
"empty": {},
|
|
||||||
"token and app provided": {
|
|
||||||
"github_token": "token",
|
|
||||||
"github_app_id": "2",
|
|
||||||
"githu_app_installation_id": "3",
|
|
||||||
"github_app_private_key": "private key",
|
|
||||||
},
|
|
||||||
"invalid app id": {
|
|
||||||
"github_app_id": "abc",
|
|
||||||
"githu_app_installation_id": "3",
|
|
||||||
"github_app_private_key": "private key",
|
|
||||||
},
|
|
||||||
"invalid app installation_id": {
|
|
||||||
"github_app_id": "1",
|
|
||||||
"githu_app_installation_id": "abc",
|
|
||||||
"github_app_private_key": "private key",
|
|
||||||
},
|
|
||||||
"empty private key": {
|
|
||||||
"github_app_id": "1",
|
|
||||||
"githu_app_installation_id": "2",
|
|
||||||
"github_app_private_key": "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, data := range tt {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
secret := &corev1.Secret{
|
|
||||||
StringData: data,
|
|
||||||
}
|
|
||||||
|
|
||||||
appConfig, err := FromSecret(secret)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Nil(t, appConfig)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppConfigFromSecret_valid(t *testing.T) {
|
|
||||||
tt := map[string]map[string]string{
|
|
||||||
"with token": {
|
|
||||||
"github_token": "token",
|
|
||||||
},
|
|
||||||
"app config": {
|
|
||||||
"github_app_id": "2",
|
|
||||||
"githu_app_installation_id": "3",
|
|
||||||
"github_app_private_key": "private key",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, data := range tt {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
secret := &corev1.Secret{
|
|
||||||
StringData: data,
|
|
||||||
}
|
|
||||||
|
|
||||||
appConfig, err := FromSecret(secret)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Nil(t, appConfig)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppConfigFromString_valid(t *testing.T) {
|
|
||||||
tt := map[string]*AppConfig{
|
|
||||||
"token": {
|
|
||||||
Token: "token",
|
|
||||||
},
|
|
||||||
"app ID": {
|
|
||||||
AppID: "1",
|
|
||||||
AppInstallationID: 2,
|
|
||||||
AppPrivateKey: "private key",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, cfg := range tt {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
bytes, err := json.Marshal(cfg)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
got, err := FromJSONString(string(bytes))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
want := cfg.tidy()
|
|
||||||
assert.Equal(t, want, got)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AutoscalingListenerSpec defines the desired state of AutoscalingListener
|
|
||||||
type AutoscalingListenerSpec struct {
|
|
||||||
// Required
|
|
||||||
GitHubConfigUrl string `json:"githubConfigUrl,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
GitHubConfigSecret string `json:"githubConfigSecret,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
RunnerScaleSetId int `json:"runnerScaleSetId,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
AutoscalingRunnerSetNamespace string `json:"autoscalingRunnerSetNamespace,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
AutoscalingRunnerSetName string `json:"autoscalingRunnerSetName,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
EphemeralRunnerSetName string `json:"ephemeralRunnerSetName,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
// +kubebuilder:validation:Minimum:=0
|
|
||||||
MaxRunners int `json:"maxRunners,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
// +kubebuilder:validation:Minimum:=0
|
|
||||||
MinRunners int `json:"minRunners,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
Image string `json:"image,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Proxy *ProxyConfig `json:"proxy,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
GitHubServerTLS *TLSConfig `json:"githubServerTLS,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
VaultConfig *VaultConfig `json:"vaultConfig,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Metrics *MetricsConfig `json:"metrics,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Template *corev1.PodTemplateSpec `json:"template,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AutoscalingListenerStatus defines the observed state of AutoscalingListener
|
|
||||||
type AutoscalingListenerStatus struct{}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
// +kubebuilder:subresource:status
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.githubConfigUrl",name=GitHub Configure URL,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.autoscalingRunnerSetNamespace",name=AutoscalingRunnerSet Namespace,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.autoscalingRunnerSetName",name=AutoscalingRunnerSet Name,type=string
|
|
||||||
|
|
||||||
// AutoscalingListener is the Schema for the autoscalinglisteners API
|
|
||||||
type AutoscalingListener struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
||||||
|
|
||||||
Spec AutoscalingListenerSpec `json:"spec,omitempty"`
|
|
||||||
Status AutoscalingListenerStatus `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
// AutoscalingListenerList contains a list of AutoscalingListener
|
|
||||||
type AutoscalingListenerList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
|
||||||
Items []AutoscalingListener `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SchemeBuilder.Register(&AutoscalingListener{}, &AutoscalingListenerList{})
|
|
||||||
}
|
|
||||||
|
|
@ -1,372 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/x509"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/hash"
|
|
||||||
"github.com/actions/actions-runner-controller/vault"
|
|
||||||
"golang.org/x/net/http/httpproxy"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
// +kubebuilder:subresource:status
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.minRunners",name=Minimum Runners,type=integer
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.maxRunners",name=Maximum Runners,type=integer
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.currentRunners",name=Current Runners,type=integer
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.state",name=State,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.pendingEphemeralRunners",name=Pending Runners,type=integer
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.runningEphemeralRunners",name=Running Runners,type=integer
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.finishedEphemeralRunners",name=Finished Runners,type=integer
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.deletingEphemeralRunners",name=Deleting Runners,type=integer
|
|
||||||
|
|
||||||
// AutoscalingRunnerSet is the Schema for the autoscalingrunnersets API
|
|
||||||
type AutoscalingRunnerSet struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
||||||
|
|
||||||
Spec AutoscalingRunnerSetSpec `json:"spec,omitempty"`
|
|
||||||
Status AutoscalingRunnerSetStatus `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AutoscalingRunnerSetSpec defines the desired state of AutoscalingRunnerSet
|
|
||||||
type AutoscalingRunnerSetSpec struct {
|
|
||||||
// Required
|
|
||||||
GitHubConfigUrl string `json:"githubConfigUrl,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
GitHubConfigSecret string `json:"githubConfigSecret,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
RunnerGroup string `json:"runnerGroup,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
RunnerScaleSetName string `json:"runnerScaleSetName,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Proxy *ProxyConfig `json:"proxy,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
GitHubServerTLS *TLSConfig `json:"githubServerTLS,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
VaultConfig *VaultConfig `json:"vaultConfig,omitempty"`
|
|
||||||
|
|
||||||
// Required
|
|
||||||
Template corev1.PodTemplateSpec `json:"template,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
ListenerMetrics *MetricsConfig `json:"listenerMetrics,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
ListenerTemplate *corev1.PodTemplateSpec `json:"listenerTemplate,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
// +kubebuilder:validation:Minimum:=0
|
|
||||||
MaxRunners *int `json:"maxRunners,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
// +kubebuilder:validation:Minimum:=0
|
|
||||||
MinRunners *int `json:"minRunners,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TLSConfig struct {
|
|
||||||
// Required
|
|
||||||
CertificateFrom *TLSCertificateSource `json:"certificateFrom,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *TLSConfig) ToCertPool(keyFetcher func(name, key string) ([]byte, error)) (*x509.CertPool, error) {
|
|
||||||
if c.CertificateFrom == nil {
|
|
||||||
return nil, fmt.Errorf("certificateFrom not specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.CertificateFrom.ConfigMapKeyRef == nil {
|
|
||||||
return nil, fmt.Errorf("configMapKeyRef not specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
cert, err := keyFetcher(c.CertificateFrom.ConfigMapKeyRef.Name, c.CertificateFrom.ConfigMapKeyRef.Key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"failed to fetch key %q in configmap %q: %w",
|
|
||||||
c.CertificateFrom.ConfigMapKeyRef.Key,
|
|
||||||
c.CertificateFrom.ConfigMapKeyRef.Name,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
systemPool, err := x509.SystemCertPool()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get system cert pool: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pool := systemPool.Clone()
|
|
||||||
if !pool.AppendCertsFromPEM(cert) {
|
|
||||||
return nil, fmt.Errorf("failed to parse certificate")
|
|
||||||
}
|
|
||||||
|
|
||||||
return pool, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type TLSCertificateSource struct {
|
|
||||||
// Required
|
|
||||||
ConfigMapKeyRef *corev1.ConfigMapKeySelector `json:"configMapKeyRef,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyConfig struct {
|
|
||||||
// +optional
|
|
||||||
HTTP *ProxyServerConfig `json:"http,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
HTTPS *ProxyServerConfig `json:"https,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
NoProxy []string `json:"noProxy,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ProxyConfig) ToHTTPProxyConfig(secretFetcher func(string) (*corev1.Secret, error)) (*httpproxy.Config, error) {
|
|
||||||
config := &httpproxy.Config{
|
|
||||||
NoProxy: strings.Join(c.NoProxy, ","),
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.HTTP != nil {
|
|
||||||
u, err := url.Parse(c.HTTP.Url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse proxy http url %q: %w", c.HTTP.Url, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.HTTP.CredentialSecretRef != "" {
|
|
||||||
secret, err := secretFetcher(c.HTTP.CredentialSecretRef)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"failed to get secret %s for http proxy: %w",
|
|
||||||
c.HTTP.CredentialSecretRef,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
u.User = url.UserPassword(
|
|
||||||
string(secret.Data["username"]),
|
|
||||||
string(secret.Data["password"]),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.HTTPProxy = u.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.HTTPS != nil {
|
|
||||||
u, err := url.Parse(c.HTTPS.Url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse proxy https url %q: %w", c.HTTPS.Url, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.HTTPS.CredentialSecretRef != "" {
|
|
||||||
secret, err := secretFetcher(c.HTTPS.CredentialSecretRef)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"failed to get secret %s for https proxy: %w",
|
|
||||||
c.HTTPS.CredentialSecretRef,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
u.User = url.UserPassword(
|
|
||||||
string(secret.Data["username"]),
|
|
||||||
string(secret.Data["password"]),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.HTTPSProxy = u.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ProxyConfig) ToSecretData(secretFetcher func(string) (*corev1.Secret, error)) (map[string][]byte, error) {
|
|
||||||
config, err := c.ToHTTPProxyConfig(secretFetcher)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
data := map[string][]byte{}
|
|
||||||
data["http_proxy"] = []byte(config.HTTPProxy)
|
|
||||||
data["https_proxy"] = []byte(config.HTTPSProxy)
|
|
||||||
data["no_proxy"] = []byte(config.NoProxy)
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ProxyConfig) ProxyFunc(secretFetcher func(string) (*corev1.Secret, error)) (func(*http.Request) (*url.URL, error), error) {
|
|
||||||
config, err := c.ToHTTPProxyConfig(secretFetcher)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyFunc := func(req *http.Request) (*url.URL, error) {
|
|
||||||
return config.ProxyFunc()(req.URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
return proxyFunc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyServerConfig struct {
|
|
||||||
// Required
|
|
||||||
Url string `json:"url,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
CredentialSecretRef string `json:"credentialSecretRef,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type VaultConfig struct {
|
|
||||||
// +optional
|
|
||||||
Type vault.VaultType `json:"type,omitempty"`
|
|
||||||
// +optional
|
|
||||||
AzureKeyVault *AzureKeyVaultConfig `json:"azureKeyVault,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Proxy *ProxyConfig `json:"proxy,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AzureKeyVaultConfig struct {
|
|
||||||
// +required
|
|
||||||
URL string `json:"url,omitempty"`
|
|
||||||
// +required
|
|
||||||
TenantID string `json:"tenantId,omitempty"`
|
|
||||||
// +required
|
|
||||||
ClientID string `json:"clientId,omitempty"`
|
|
||||||
// +required
|
|
||||||
CertificatePath string `json:"certificatePath,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MetricsConfig holds configuration parameters for each metric type
|
|
||||||
type MetricsConfig struct {
|
|
||||||
// +optional
|
|
||||||
Counters map[string]*CounterMetric `json:"counters,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Gauges map[string]*GaugeMetric `json:"gauges,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Histograms map[string]*HistogramMetric `json:"histograms,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CounterMetric holds configuration of a single metric of type Counter
|
|
||||||
type CounterMetric struct {
|
|
||||||
Labels []string `json:"labels"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GaugeMetric holds configuration of a single metric of type Gauge
|
|
||||||
type GaugeMetric struct {
|
|
||||||
Labels []string `json:"labels"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HistogramMetric holds configuration of a single metric of type Histogram
|
|
||||||
type HistogramMetric struct {
|
|
||||||
Labels []string `json:"labels"`
|
|
||||||
Buckets []float64 `json:"buckets,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet
|
|
||||||
type AutoscalingRunnerSetStatus struct {
|
|
||||||
// +optional
|
|
||||||
CurrentRunners int `json:"currentRunners"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
State string `json:"state"`
|
|
||||||
|
|
||||||
// EphemeralRunner counts separated by the stage ephemeral runners are in, taken from the EphemeralRunnerSet
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
PendingEphemeralRunners int `json:"pendingEphemeralRunners"`
|
|
||||||
// +optional
|
|
||||||
RunningEphemeralRunners int `json:"runningEphemeralRunners"`
|
|
||||||
// +optional
|
|
||||||
FailedEphemeralRunners int `json:"failedEphemeralRunners"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ars *AutoscalingRunnerSet) ListenerSpecHash() string {
|
|
||||||
arsSpec := ars.Spec.DeepCopy()
|
|
||||||
spec := arsSpec
|
|
||||||
return hash.ComputeTemplateHash(&spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ars *AutoscalingRunnerSet) GitHubConfigSecret() string {
|
|
||||||
return ars.Spec.GitHubConfigSecret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ars *AutoscalingRunnerSet) GitHubConfigUrl() string {
|
|
||||||
return ars.Spec.GitHubConfigUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ars *AutoscalingRunnerSet) GitHubProxy() *ProxyConfig {
|
|
||||||
return ars.Spec.Proxy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ars *AutoscalingRunnerSet) GitHubServerTLS() *TLSConfig {
|
|
||||||
return ars.Spec.GitHubServerTLS
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ars *AutoscalingRunnerSet) VaultConfig() *VaultConfig {
|
|
||||||
return ars.Spec.VaultConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ars *AutoscalingRunnerSet) VaultProxy() *ProxyConfig {
|
|
||||||
if ars.Spec.VaultConfig != nil {
|
|
||||||
return ars.Spec.VaultConfig.Proxy
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ars *AutoscalingRunnerSet) RunnerSetSpecHash() string {
|
|
||||||
type runnerSetSpec struct {
|
|
||||||
GitHubConfigUrl string
|
|
||||||
GitHubConfigSecret string
|
|
||||||
RunnerGroup string
|
|
||||||
RunnerScaleSetName string
|
|
||||||
Proxy *ProxyConfig
|
|
||||||
GitHubServerTLS *TLSConfig
|
|
||||||
Template corev1.PodTemplateSpec
|
|
||||||
}
|
|
||||||
spec := &runnerSetSpec{
|
|
||||||
GitHubConfigUrl: ars.Spec.GitHubConfigUrl,
|
|
||||||
GitHubConfigSecret: ars.Spec.GitHubConfigSecret,
|
|
||||||
RunnerGroup: ars.Spec.RunnerGroup,
|
|
||||||
RunnerScaleSetName: ars.Spec.RunnerScaleSetName,
|
|
||||||
Proxy: ars.Spec.Proxy,
|
|
||||||
GitHubServerTLS: ars.Spec.GitHubServerTLS,
|
|
||||||
Template: ars.Spec.Template,
|
|
||||||
}
|
|
||||||
return hash.ComputeTemplateHash(&spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
|
|
||||||
// AutoscalingRunnerSetList contains a list of AutoscalingRunnerSet
|
|
||||||
type AutoscalingRunnerSetList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
|
||||||
Items []AutoscalingRunnerSet `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SchemeBuilder.Register(&AutoscalingRunnerSet{}, &AutoscalingRunnerSetList{})
|
|
||||||
}
|
|
||||||
|
|
@ -1,193 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EphemeralRunnerContainerName is the name of the runner container.
|
|
||||||
// It represents the name of the container running the self-hosted runner image.
|
|
||||||
const EphemeralRunnerContainerName = "runner"
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
// +kubebuilder:subresource:status
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.githubConfigUrl",name="GitHub Config URL",type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.runnerId",name=RunnerId,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Status,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.jobRepositoryName",name=JobRepository,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.jobWorkflowRef",name=JobWorkflowRef,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.workflowRunId",name=WorkflowRunId,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.jobDisplayName",name=JobDisplayName,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.message",name=Message,type=string
|
|
||||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
|
||||||
|
|
||||||
// EphemeralRunner is the Schema for the ephemeralrunners API
|
|
||||||
type EphemeralRunner struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
||||||
|
|
||||||
Spec EphemeralRunnerSpec `json:"spec,omitempty"`
|
|
||||||
Status EphemeralRunnerStatus `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (er *EphemeralRunner) IsDone() bool {
|
|
||||||
return er.Status.Phase == corev1.PodSucceeded || er.Status.Phase == corev1.PodFailed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (er *EphemeralRunner) HasContainerHookConfigured() bool {
|
|
||||||
for i := range er.Spec.Spec.Containers {
|
|
||||||
if er.Spec.Spec.Containers[i].Name != EphemeralRunnerContainerName {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, env := range er.Spec.Spec.Containers[i].Env {
|
|
||||||
if env.Name == "ACTIONS_RUNNER_CONTAINER_HOOKS" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (er *EphemeralRunner) GitHubConfigSecret() string {
|
|
||||||
return er.Spec.GitHubConfigSecret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (er *EphemeralRunner) GitHubConfigUrl() string {
|
|
||||||
return er.Spec.GitHubConfigUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
func (er *EphemeralRunner) GitHubProxy() *ProxyConfig {
|
|
||||||
return er.Spec.Proxy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (er *EphemeralRunner) GitHubServerTLS() *TLSConfig {
|
|
||||||
return er.Spec.GitHubServerTLS
|
|
||||||
}
|
|
||||||
|
|
||||||
func (er *EphemeralRunner) VaultConfig() *VaultConfig {
|
|
||||||
return er.Spec.VaultConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (er *EphemeralRunner) VaultProxy() *ProxyConfig {
|
|
||||||
if er.Spec.VaultConfig != nil {
|
|
||||||
return er.Spec.VaultConfig.Proxy
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EphemeralRunnerSpec defines the desired state of EphemeralRunner
|
|
||||||
type EphemeralRunnerSpec struct {
|
|
||||||
// +required
|
|
||||||
GitHubConfigUrl string `json:"githubConfigUrl,omitempty"`
|
|
||||||
|
|
||||||
// +required
|
|
||||||
GitHubConfigSecret string `json:"githubConfigSecret,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
GitHubServerTLS *TLSConfig `json:"githubServerTLS,omitempty"`
|
|
||||||
|
|
||||||
// +required
|
|
||||||
RunnerScaleSetId int `json:"runnerScaleSetId,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Proxy *ProxyConfig `json:"proxy,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
ProxySecretRef string `json:"proxySecretRef,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
VaultConfig *VaultConfig `json:"vaultConfig,omitempty"`
|
|
||||||
|
|
||||||
corev1.PodTemplateSpec `json:",inline"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// EphemeralRunnerStatus defines the observed state of EphemeralRunner
|
|
||||||
type EphemeralRunnerStatus struct {
|
|
||||||
// Turns true only if the runner is online.
|
|
||||||
// +optional
|
|
||||||
Ready bool `json:"ready"`
|
|
||||||
// Phase describes phases where EphemeralRunner can be in.
|
|
||||||
// The underlying type is a PodPhase, but the meaning is more restrictive
|
|
||||||
//
|
|
||||||
// The PodFailed phase should be set only when EphemeralRunner fails to start
|
|
||||||
// after multiple retries. That signals that this EphemeralRunner won't work,
|
|
||||||
// and manual inspection is required
|
|
||||||
//
|
|
||||||
// The PodSucceded phase should be set only when confirmed that EphemeralRunner
|
|
||||||
// actually executed the job and has been removed from the service.
|
|
||||||
// +optional
|
|
||||||
Phase corev1.PodPhase `json:"phase,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Reason string `json:"reason,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
RunnerId int `json:"runnerId,omitempty"`
|
|
||||||
// +optional
|
|
||||||
RunnerName string `json:"runnerName,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Failures map[string]metav1.Time `json:"failures,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
JobRequestId int64 `json:"jobRequestId,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
JobRepositoryName string `json:"jobRepositoryName,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
JobWorkflowRef string `json:"jobWorkflowRef,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
WorkflowRunId int64 `json:"workflowRunId,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
JobDisplayName string `json:"jobDisplayName,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *EphemeralRunnerStatus) LastFailure() metav1.Time {
|
|
||||||
var maxTime metav1.Time
|
|
||||||
if len(s.Failures) == 0 {
|
|
||||||
return maxTime
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ts := range s.Failures {
|
|
||||||
if ts.After(maxTime.Time) {
|
|
||||||
maxTime = ts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return maxTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
|
|
||||||
// EphemeralRunnerList contains a list of EphemeralRunner
|
|
||||||
type EphemeralRunnerList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
|
||||||
Items []EphemeralRunner `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SchemeBuilder.Register(&EphemeralRunner{}, &EphemeralRunnerList{})
|
|
||||||
}
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EphemeralRunnerSetSpec defines the desired state of EphemeralRunnerSet
|
|
||||||
type EphemeralRunnerSetSpec struct {
|
|
||||||
// Replicas is the number of desired EphemeralRunner resources in the k8s namespace.
|
|
||||||
Replicas int `json:"replicas,omitempty"`
|
|
||||||
// PatchID is the unique identifier for the patch issued by the listener app
|
|
||||||
PatchID int `json:"patchID"`
|
|
||||||
// EphemeralRunnerSpec is the spec of the ephemeral runner
|
|
||||||
EphemeralRunnerSpec EphemeralRunnerSpec `json:"ephemeralRunnerSpec,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet
|
|
||||||
type EphemeralRunnerSetStatus struct {
|
|
||||||
// CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet.
|
|
||||||
CurrentReplicas int `json:"currentReplicas"`
|
|
||||||
// +optional
|
|
||||||
PendingEphemeralRunners int `json:"pendingEphemeralRunners"`
|
|
||||||
// +optional
|
|
||||||
RunningEphemeralRunners int `json:"runningEphemeralRunners"`
|
|
||||||
// +optional
|
|
||||||
FailedEphemeralRunners int `json:"failedEphemeralRunners"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
// +kubebuilder:subresource:status
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="DesiredReplicas",type="integer"
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.currentReplicas", name="CurrentReplicas",type="integer"
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.pendingEphemeralRunners",name=Pending Runners,type=integer
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.runningEphemeralRunners",name=Running Runners,type=integer
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.finishedEphemeralRunners",name=Finished Runners,type=integer
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.deletingEphemeralRunners",name=Deleting Runners,type=integer
|
|
||||||
|
|
||||||
// EphemeralRunnerSet is the Schema for the ephemeralrunnersets API
|
|
||||||
type EphemeralRunnerSet struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
||||||
|
|
||||||
Spec EphemeralRunnerSetSpec `json:"spec,omitempty"`
|
|
||||||
Status EphemeralRunnerSetStatus `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ers *EphemeralRunnerSet) GitHubConfigSecret() string {
|
|
||||||
return ers.Spec.EphemeralRunnerSpec.GitHubConfigSecret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ers *EphemeralRunnerSet) GitHubConfigUrl() string {
|
|
||||||
return ers.Spec.EphemeralRunnerSpec.GitHubConfigUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ers *EphemeralRunnerSet) GitHubProxy() *ProxyConfig {
|
|
||||||
return ers.Spec.EphemeralRunnerSpec.Proxy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ers *EphemeralRunnerSet) GitHubServerTLS() *TLSConfig {
|
|
||||||
return ers.Spec.EphemeralRunnerSpec.GitHubServerTLS
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ers *EphemeralRunnerSet) VaultConfig() *VaultConfig {
|
|
||||||
return ers.Spec.EphemeralRunnerSpec.VaultConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ers *EphemeralRunnerSet) VaultProxy() *ProxyConfig {
|
|
||||||
if ers.Spec.EphemeralRunnerSpec.VaultConfig != nil {
|
|
||||||
return ers.Spec.EphemeralRunnerSpec.VaultConfig.Proxy
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EphemeralRunnerSetList contains a list of EphemeralRunnerSet
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
type EphemeralRunnerSetList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
|
||||||
Items []EphemeralRunnerSet `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SchemeBuilder.Register(&EphemeralRunnerSet{}, &EphemeralRunnerSetList{})
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1 contains API Schema definitions for the batch v1 API group
|
|
||||||
// +kubebuilder:object:generate=true
|
|
||||||
// +groupName=actions.github.com
|
|
||||||
package v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// GroupVersion is group version used to register these objects
|
|
||||||
GroupVersion = schema.GroupVersion{Group: "actions.github.com", Version: "v1alpha1"}
|
|
||||||
|
|
||||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
|
||||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
|
||||||
|
|
||||||
// AddToScheme adds the types in this group-version to the given scheme.
|
|
||||||
AddToScheme = SchemeBuilder.AddToScheme
|
|
||||||
)
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
package v1alpha1_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestProxyConfig_ToSecret(t *testing.T) {
|
|
||||||
config := &v1alpha1.ProxyConfig{
|
|
||||||
HTTP: &v1alpha1.ProxyServerConfig{
|
|
||||||
Url: "http://proxy.example.com:8080",
|
|
||||||
CredentialSecretRef: "my-secret",
|
|
||||||
},
|
|
||||||
HTTPS: &v1alpha1.ProxyServerConfig{
|
|
||||||
Url: "https://proxy.example.com:8080",
|
|
||||||
CredentialSecretRef: "my-secret",
|
|
||||||
},
|
|
||||||
NoProxy: []string{
|
|
||||||
"noproxy.example.com",
|
|
||||||
"noproxy2.example.com",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
secretFetcher := func(string) (*corev1.Secret, error) {
|
|
||||||
return &corev1.Secret{
|
|
||||||
Data: map[string][]byte{
|
|
||||||
"username": []byte("username"),
|
|
||||||
"password": []byte("password"),
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := config.ToSecretData(secretFetcher)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, result)
|
|
||||||
|
|
||||||
assert.Equal(t, "http://username:password@proxy.example.com:8080", string(result["http_proxy"]))
|
|
||||||
assert.Equal(t, "https://username:password@proxy.example.com:8080", string(result["https_proxy"]))
|
|
||||||
assert.Equal(t, "noproxy.example.com,noproxy2.example.com", string(result["no_proxy"]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProxyConfig_ProxyFunc(t *testing.T) {
|
|
||||||
config := &v1alpha1.ProxyConfig{
|
|
||||||
HTTP: &v1alpha1.ProxyServerConfig{
|
|
||||||
Url: "http://proxy.example.com:8080",
|
|
||||||
CredentialSecretRef: "my-secret",
|
|
||||||
},
|
|
||||||
HTTPS: &v1alpha1.ProxyServerConfig{
|
|
||||||
Url: "https://proxy.example.com:8080",
|
|
||||||
CredentialSecretRef: "my-secret",
|
|
||||||
},
|
|
||||||
NoProxy: []string{
|
|
||||||
"noproxy.example.com",
|
|
||||||
"noproxy2.example.com",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
secretFetcher := func(string) (*corev1.Secret, error) {
|
|
||||||
return &corev1.Secret{
|
|
||||||
Data: map[string][]byte{
|
|
||||||
"username": []byte("username"),
|
|
||||||
"password": []byte("password"),
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := config.ProxyFunc(secretFetcher)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
in string
|
|
||||||
out string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "http target",
|
|
||||||
in: "http://target.com",
|
|
||||||
out: "http://username:password@proxy.example.com:8080",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "https target",
|
|
||||||
in: "https://target.com",
|
|
||||||
out: "https://username:password@proxy.example.com:8080",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no proxy",
|
|
||||||
in: "https://noproxy.example.com",
|
|
||||||
out: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no proxy 2",
|
|
||||||
in: "https://noproxy2.example.com",
|
|
||||||
out: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
req, err := http.NewRequest("GET", test.in, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
u, err := result(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
if test.out == "" {
|
|
||||||
assert.Nil(t, u)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, test.out, u.String())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
package v1alpha1_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
|
||||||
"github.com/actions/actions-runner-controller/github/actions/testserver"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGitHubServerTLSConfig_ToCertPool(t *testing.T) {
|
|
||||||
t.Run("returns an error if CertificateFrom not specified", func(t *testing.T) {
|
|
||||||
c := &v1alpha1.TLSConfig{
|
|
||||||
CertificateFrom: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
pool, err := c.ToCertPool(nil)
|
|
||||||
assert.Nil(t, pool)
|
|
||||||
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.Equal(t, err.Error(), "certificateFrom not specified")
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("returns an error if CertificateFrom.ConfigMapKeyRef not specified", func(t *testing.T) {
|
|
||||||
c := &v1alpha1.TLSConfig{
|
|
||||||
CertificateFrom: &v1alpha1.TLSCertificateSource{},
|
|
||||||
}
|
|
||||||
|
|
||||||
pool, err := c.ToCertPool(nil)
|
|
||||||
assert.Nil(t, pool)
|
|
||||||
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.Equal(t, err.Error(), "configMapKeyRef not specified")
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("returns a valid cert pool with correct configuration", func(t *testing.T) {
|
|
||||||
c := &v1alpha1.TLSConfig{
|
|
||||||
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
|
||||||
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
|
|
||||||
LocalObjectReference: v1.LocalObjectReference{
|
|
||||||
Name: "name",
|
|
||||||
},
|
|
||||||
Key: "key",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
certsFolder := filepath.Join(
|
|
||||||
"../../../",
|
|
||||||
"github",
|
|
||||||
"actions",
|
|
||||||
"testdata",
|
|
||||||
)
|
|
||||||
|
|
||||||
fetcher := func(name, key string) ([]byte, error) {
|
|
||||||
cert, err := os.ReadFile(filepath.Join(certsFolder, "rootCA.crt"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
pool := x509.NewCertPool()
|
|
||||||
ok := pool.AppendCertsFromPEM(cert)
|
|
||||||
assert.True(t, ok)
|
|
||||||
|
|
||||||
return cert, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
pool, err := c.ToCertPool(fetcher)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, pool)
|
|
||||||
|
|
||||||
// can be used to communicate with a server
|
|
||||||
serverSuccessfullyCalled := false
|
|
||||||
server := testserver.NewUnstarted(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
serverSuccessfullyCalled = true
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}))
|
|
||||||
|
|
||||||
cert, err := tls.LoadX509KeyPair(
|
|
||||||
filepath.Join(certsFolder, "server.crt"),
|
|
||||||
filepath.Join(certsFolder, "server.key"),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
|
||||||
server.StartTLS()
|
|
||||||
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
RootCAs: pool,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = client.Get(server.URL)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.True(t, serverSuccessfullyCalled)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
package v1alpha1
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
func IsVersionAllowed(resourceVersion, buildVersion string) bool {
|
|
||||||
if buildVersion == "dev" || resourceVersion == buildVersion || strings.HasPrefix(buildVersion, "canary-") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
rv, ok := parseSemver(resourceVersion)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
bv, ok := parseSemver(buildVersion)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return rv.major == bv.major && rv.minor == bv.minor
|
|
||||||
}
|
|
||||||
|
|
||||||
type semver struct {
|
|
||||||
major string
|
|
||||||
minor string
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSemver(v string) (p semver, ok bool) {
|
|
||||||
if v == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.major, v, ok = parseInt(v)
|
|
||||||
if !ok {
|
|
||||||
return p, false
|
|
||||||
}
|
|
||||||
if v == "" {
|
|
||||||
p.minor = "0"
|
|
||||||
return p, true
|
|
||||||
}
|
|
||||||
if v[0] != '.' {
|
|
||||||
return p, false
|
|
||||||
}
|
|
||||||
p.minor, v, ok = parseInt(v[1:])
|
|
||||||
if !ok {
|
|
||||||
return p, false
|
|
||||||
}
|
|
||||||
if v == "" {
|
|
||||||
return p, true
|
|
||||||
}
|
|
||||||
if v[0] != '.' {
|
|
||||||
return p, false
|
|
||||||
}
|
|
||||||
if _, _, ok = parseInt(v[1:]); !ok {
|
|
||||||
return p, false
|
|
||||||
}
|
|
||||||
return p, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseInt(v string) (t, rest string, ok bool) {
|
|
||||||
if v == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[0] < '0' || '9' < v[0] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if v[0] == '0' && i != 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return v[:i], v[i:], true
|
|
||||||
}
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
package v1alpha1_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIsVersionAllowed(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
tt := map[string]struct {
|
|
||||||
resourceVersion string
|
|
||||||
buildVersion string
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
"dev should always be allowed": {
|
|
||||||
resourceVersion: "0.11.0",
|
|
||||||
buildVersion: "dev",
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
"resourceVersion is not semver": {
|
|
||||||
resourceVersion: "dev",
|
|
||||||
buildVersion: "0.11.0",
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
"buildVersion is not semver": {
|
|
||||||
resourceVersion: "0.11.0",
|
|
||||||
buildVersion: "NA",
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
"major version mismatch": {
|
|
||||||
resourceVersion: "0.11.0",
|
|
||||||
buildVersion: "1.11.0",
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
"minor version mismatch": {
|
|
||||||
resourceVersion: "0.11.0",
|
|
||||||
buildVersion: "0.10.0",
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
"patch version mismatch": {
|
|
||||||
resourceVersion: "0.11.1",
|
|
||||||
buildVersion: "0.11.0",
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
"arbitrary version match": {
|
|
||||||
resourceVersion: "abc",
|
|
||||||
buildVersion: "abc",
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, tc := range tt {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
got := v1alpha1.IsVersionAllowed(tc.resourceVersion, tc.buildVersion)
|
|
||||||
assert.Equal(t, tc.want, got)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,726 +0,0 @@
|
||||||
//go:build !ignore_autogenerated
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Code generated by controller-gen. DO NOT EDIT.
|
|
||||||
|
|
||||||
package v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *AutoscalingListener) DeepCopyInto(out *AutoscalingListener) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
|
||||||
in.Spec.DeepCopyInto(&out.Spec)
|
|
||||||
out.Status = in.Status
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingListener.
|
|
||||||
func (in *AutoscalingListener) DeepCopy() *AutoscalingListener {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(AutoscalingListener)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *AutoscalingListener) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *AutoscalingListenerList) DeepCopyInto(out *AutoscalingListenerList) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
|
||||||
if in.Items != nil {
|
|
||||||
in, out := &in.Items, &out.Items
|
|
||||||
*out = make([]AutoscalingListener, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingListenerList.
|
|
||||||
func (in *AutoscalingListenerList) DeepCopy() *AutoscalingListenerList {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(AutoscalingListenerList)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *AutoscalingListenerList) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *AutoscalingListenerSpec) DeepCopyInto(out *AutoscalingListenerSpec) {
|
|
||||||
*out = *in
|
|
||||||
if in.ImagePullSecrets != nil {
|
|
||||||
in, out := &in.ImagePullSecrets, &out.ImagePullSecrets
|
|
||||||
*out = make([]v1.LocalObjectReference, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.Proxy != nil {
|
|
||||||
in, out := &in.Proxy, &out.Proxy
|
|
||||||
*out = new(ProxyConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.GitHubServerTLS != nil {
|
|
||||||
in, out := &in.GitHubServerTLS, &out.GitHubServerTLS
|
|
||||||
*out = new(TLSConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.VaultConfig != nil {
|
|
||||||
in, out := &in.VaultConfig, &out.VaultConfig
|
|
||||||
*out = new(VaultConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.Metrics != nil {
|
|
||||||
in, out := &in.Metrics, &out.Metrics
|
|
||||||
*out = new(MetricsConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.Template != nil {
|
|
||||||
in, out := &in.Template, &out.Template
|
|
||||||
*out = new(v1.PodTemplateSpec)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingListenerSpec.
|
|
||||||
func (in *AutoscalingListenerSpec) DeepCopy() *AutoscalingListenerSpec {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(AutoscalingListenerSpec)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *AutoscalingListenerStatus) DeepCopyInto(out *AutoscalingListenerStatus) {
|
|
||||||
*out = *in
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingListenerStatus.
|
|
||||||
func (in *AutoscalingListenerStatus) DeepCopy() *AutoscalingListenerStatus {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(AutoscalingListenerStatus)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *AutoscalingRunnerSet) DeepCopyInto(out *AutoscalingRunnerSet) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
|
||||||
in.Spec.DeepCopyInto(&out.Spec)
|
|
||||||
out.Status = in.Status
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingRunnerSet.
|
|
||||||
func (in *AutoscalingRunnerSet) DeepCopy() *AutoscalingRunnerSet {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(AutoscalingRunnerSet)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *AutoscalingRunnerSet) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *AutoscalingRunnerSetList) DeepCopyInto(out *AutoscalingRunnerSetList) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
|
||||||
if in.Items != nil {
|
|
||||||
in, out := &in.Items, &out.Items
|
|
||||||
*out = make([]AutoscalingRunnerSet, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingRunnerSetList.
|
|
||||||
func (in *AutoscalingRunnerSetList) DeepCopy() *AutoscalingRunnerSetList {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(AutoscalingRunnerSetList)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *AutoscalingRunnerSetList) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *AutoscalingRunnerSetSpec) DeepCopyInto(out *AutoscalingRunnerSetSpec) {
|
|
||||||
*out = *in
|
|
||||||
if in.Proxy != nil {
|
|
||||||
in, out := &in.Proxy, &out.Proxy
|
|
||||||
*out = new(ProxyConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.GitHubServerTLS != nil {
|
|
||||||
in, out := &in.GitHubServerTLS, &out.GitHubServerTLS
|
|
||||||
*out = new(TLSConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.VaultConfig != nil {
|
|
||||||
in, out := &in.VaultConfig, &out.VaultConfig
|
|
||||||
*out = new(VaultConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
in.Template.DeepCopyInto(&out.Template)
|
|
||||||
if in.ListenerMetrics != nil {
|
|
||||||
in, out := &in.ListenerMetrics, &out.ListenerMetrics
|
|
||||||
*out = new(MetricsConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.ListenerTemplate != nil {
|
|
||||||
in, out := &in.ListenerTemplate, &out.ListenerTemplate
|
|
||||||
*out = new(v1.PodTemplateSpec)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.MaxRunners != nil {
|
|
||||||
in, out := &in.MaxRunners, &out.MaxRunners
|
|
||||||
*out = new(int)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.MinRunners != nil {
|
|
||||||
in, out := &in.MinRunners, &out.MinRunners
|
|
||||||
*out = new(int)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingRunnerSetSpec.
|
|
||||||
func (in *AutoscalingRunnerSetSpec) DeepCopy() *AutoscalingRunnerSetSpec {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(AutoscalingRunnerSetSpec)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *AutoscalingRunnerSetStatus) DeepCopyInto(out *AutoscalingRunnerSetStatus) {
|
|
||||||
*out = *in
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingRunnerSetStatus.
|
|
||||||
func (in *AutoscalingRunnerSetStatus) DeepCopy() *AutoscalingRunnerSetStatus {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(AutoscalingRunnerSetStatus)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *AzureKeyVaultConfig) DeepCopyInto(out *AzureKeyVaultConfig) {
|
|
||||||
*out = *in
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureKeyVaultConfig.
|
|
||||||
func (in *AzureKeyVaultConfig) DeepCopy() *AzureKeyVaultConfig {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(AzureKeyVaultConfig)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *CounterMetric) DeepCopyInto(out *CounterMetric) {
|
|
||||||
*out = *in
|
|
||||||
if in.Labels != nil {
|
|
||||||
in, out := &in.Labels, &out.Labels
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CounterMetric.
|
|
||||||
func (in *CounterMetric) DeepCopy() *CounterMetric {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(CounterMetric)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *EphemeralRunner) DeepCopyInto(out *EphemeralRunner) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
|
||||||
in.Spec.DeepCopyInto(&out.Spec)
|
|
||||||
in.Status.DeepCopyInto(&out.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunner.
|
|
||||||
func (in *EphemeralRunner) DeepCopy() *EphemeralRunner {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(EphemeralRunner)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *EphemeralRunner) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *EphemeralRunnerList) DeepCopyInto(out *EphemeralRunnerList) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
|
||||||
if in.Items != nil {
|
|
||||||
in, out := &in.Items, &out.Items
|
|
||||||
*out = make([]EphemeralRunner, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerList.
|
|
||||||
func (in *EphemeralRunnerList) DeepCopy() *EphemeralRunnerList {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(EphemeralRunnerList)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *EphemeralRunnerList) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *EphemeralRunnerSet) DeepCopyInto(out *EphemeralRunnerSet) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
|
||||||
in.Spec.DeepCopyInto(&out.Spec)
|
|
||||||
out.Status = in.Status
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerSet.
|
|
||||||
func (in *EphemeralRunnerSet) DeepCopy() *EphemeralRunnerSet {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(EphemeralRunnerSet)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *EphemeralRunnerSet) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *EphemeralRunnerSetList) DeepCopyInto(out *EphemeralRunnerSetList) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
|
||||||
if in.Items != nil {
|
|
||||||
in, out := &in.Items, &out.Items
|
|
||||||
*out = make([]EphemeralRunnerSet, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerSetList.
|
|
||||||
func (in *EphemeralRunnerSetList) DeepCopy() *EphemeralRunnerSetList {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(EphemeralRunnerSetList)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *EphemeralRunnerSetList) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *EphemeralRunnerSetSpec) DeepCopyInto(out *EphemeralRunnerSetSpec) {
|
|
||||||
*out = *in
|
|
||||||
in.EphemeralRunnerSpec.DeepCopyInto(&out.EphemeralRunnerSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerSetSpec.
|
|
||||||
func (in *EphemeralRunnerSetSpec) DeepCopy() *EphemeralRunnerSetSpec {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(EphemeralRunnerSetSpec)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *EphemeralRunnerSetStatus) DeepCopyInto(out *EphemeralRunnerSetStatus) {
|
|
||||||
*out = *in
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerSetStatus.
|
|
||||||
func (in *EphemeralRunnerSetStatus) DeepCopy() *EphemeralRunnerSetStatus {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(EphemeralRunnerSetStatus)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *EphemeralRunnerSpec) DeepCopyInto(out *EphemeralRunnerSpec) {
|
|
||||||
*out = *in
|
|
||||||
if in.GitHubServerTLS != nil {
|
|
||||||
in, out := &in.GitHubServerTLS, &out.GitHubServerTLS
|
|
||||||
*out = new(TLSConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.Proxy != nil {
|
|
||||||
in, out := &in.Proxy, &out.Proxy
|
|
||||||
*out = new(ProxyConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.VaultConfig != nil {
|
|
||||||
in, out := &in.VaultConfig, &out.VaultConfig
|
|
||||||
*out = new(VaultConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
in.PodTemplateSpec.DeepCopyInto(&out.PodTemplateSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerSpec.
|
|
||||||
func (in *EphemeralRunnerSpec) DeepCopy() *EphemeralRunnerSpec {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(EphemeralRunnerSpec)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *EphemeralRunnerStatus) DeepCopyInto(out *EphemeralRunnerStatus) {
|
|
||||||
*out = *in
|
|
||||||
if in.Failures != nil {
|
|
||||||
in, out := &in.Failures, &out.Failures
|
|
||||||
*out = make(map[string]metav1.Time, len(*in))
|
|
||||||
for key, val := range *in {
|
|
||||||
(*out)[key] = *val.DeepCopy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerStatus.
|
|
||||||
func (in *EphemeralRunnerStatus) DeepCopy() *EphemeralRunnerStatus {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(EphemeralRunnerStatus)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *GaugeMetric) DeepCopyInto(out *GaugeMetric) {
|
|
||||||
*out = *in
|
|
||||||
if in.Labels != nil {
|
|
||||||
in, out := &in.Labels, &out.Labels
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GaugeMetric.
|
|
||||||
func (in *GaugeMetric) DeepCopy() *GaugeMetric {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(GaugeMetric)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *HistogramMetric) DeepCopyInto(out *HistogramMetric) {
|
|
||||||
*out = *in
|
|
||||||
if in.Labels != nil {
|
|
||||||
in, out := &in.Labels, &out.Labels
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.Buckets != nil {
|
|
||||||
in, out := &in.Buckets, &out.Buckets
|
|
||||||
*out = make([]float64, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HistogramMetric.
|
|
||||||
func (in *HistogramMetric) DeepCopy() *HistogramMetric {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(HistogramMetric)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *MetricsConfig) DeepCopyInto(out *MetricsConfig) {
|
|
||||||
*out = *in
|
|
||||||
if in.Counters != nil {
|
|
||||||
in, out := &in.Counters, &out.Counters
|
|
||||||
*out = make(map[string]*CounterMetric, len(*in))
|
|
||||||
for key, val := range *in {
|
|
||||||
var outVal *CounterMetric
|
|
||||||
if val == nil {
|
|
||||||
(*out)[key] = nil
|
|
||||||
} else {
|
|
||||||
inVal := (*in)[key]
|
|
||||||
in, out := &inVal, &outVal
|
|
||||||
*out = new(CounterMetric)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
(*out)[key] = outVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.Gauges != nil {
|
|
||||||
in, out := &in.Gauges, &out.Gauges
|
|
||||||
*out = make(map[string]*GaugeMetric, len(*in))
|
|
||||||
for key, val := range *in {
|
|
||||||
var outVal *GaugeMetric
|
|
||||||
if val == nil {
|
|
||||||
(*out)[key] = nil
|
|
||||||
} else {
|
|
||||||
inVal := (*in)[key]
|
|
||||||
in, out := &inVal, &outVal
|
|
||||||
*out = new(GaugeMetric)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
(*out)[key] = outVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.Histograms != nil {
|
|
||||||
in, out := &in.Histograms, &out.Histograms
|
|
||||||
*out = make(map[string]*HistogramMetric, len(*in))
|
|
||||||
for key, val := range *in {
|
|
||||||
var outVal *HistogramMetric
|
|
||||||
if val == nil {
|
|
||||||
(*out)[key] = nil
|
|
||||||
} else {
|
|
||||||
inVal := (*in)[key]
|
|
||||||
in, out := &inVal, &outVal
|
|
||||||
*out = new(HistogramMetric)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
(*out)[key] = outVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsConfig.
|
|
||||||
func (in *MetricsConfig) DeepCopy() *MetricsConfig {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(MetricsConfig)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *ProxyConfig) DeepCopyInto(out *ProxyConfig) {
|
|
||||||
*out = *in
|
|
||||||
if in.HTTP != nil {
|
|
||||||
in, out := &in.HTTP, &out.HTTP
|
|
||||||
*out = new(ProxyServerConfig)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.HTTPS != nil {
|
|
||||||
in, out := &in.HTTPS, &out.HTTPS
|
|
||||||
*out = new(ProxyServerConfig)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.NoProxy != nil {
|
|
||||||
in, out := &in.NoProxy, &out.NoProxy
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyConfig.
|
|
||||||
func (in *ProxyConfig) DeepCopy() *ProxyConfig {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(ProxyConfig)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *ProxyServerConfig) DeepCopyInto(out *ProxyServerConfig) {
|
|
||||||
*out = *in
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyServerConfig.
|
|
||||||
func (in *ProxyServerConfig) DeepCopy() *ProxyServerConfig {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(ProxyServerConfig)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *TLSCertificateSource) DeepCopyInto(out *TLSCertificateSource) {
|
|
||||||
*out = *in
|
|
||||||
if in.ConfigMapKeyRef != nil {
|
|
||||||
in, out := &in.ConfigMapKeyRef, &out.ConfigMapKeyRef
|
|
||||||
*out = new(v1.ConfigMapKeySelector)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCertificateSource.
|
|
||||||
func (in *TLSCertificateSource) DeepCopy() *TLSCertificateSource {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(TLSCertificateSource)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *TLSConfig) DeepCopyInto(out *TLSConfig) {
|
|
||||||
*out = *in
|
|
||||||
if in.CertificateFrom != nil {
|
|
||||||
in, out := &in.CertificateFrom, &out.CertificateFrom
|
|
||||||
*out = new(TLSCertificateSource)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSConfig.
|
|
||||||
func (in *TLSConfig) DeepCopy() *TLSConfig {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(TLSConfig)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *VaultConfig) DeepCopyInto(out *VaultConfig) {
|
|
||||||
*out = *in
|
|
||||||
if in.AzureKeyVault != nil {
|
|
||||||
in, out := &in.AzureKeyVault, &out.AzureKeyVault
|
|
||||||
*out = new(AzureKeyVaultConfig)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.Proxy != nil {
|
|
||||||
in, out := &in.Proxy, &out.Proxy
|
|
||||||
*out = new(ProxyConfig)
|
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultConfig.
|
|
||||||
func (in *VaultConfig) DeepCopy() *VaultConfig {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(VaultConfig)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1 contains API Schema definitions for the actions v1alpha1 API group
|
|
||||||
// +kubebuilder:object:generate=true
|
|
||||||
// +groupName=actions.summerwind.dev
|
|
||||||
package v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// GroupVersion is group version used to register these objects
|
|
||||||
GroupVersion = schema.GroupVersion{Group: "actions.summerwind.dev", Version: "v1alpha1"}
|
|
||||||
|
|
||||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
|
||||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
|
||||||
|
|
||||||
// AddToScheme adds the types in this group-version to the given scheme.
|
|
||||||
AddToScheme = SchemeBuilder.AddToScheme
|
|
||||||
)
|
|
||||||
|
|
@ -1,269 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HorizontalRunnerAutoscalerSpec defines the desired state of HorizontalRunnerAutoscaler
|
|
||||||
type HorizontalRunnerAutoscalerSpec struct {
|
|
||||||
// ScaleTargetRef is the reference to scaled resource like RunnerDeployment
|
|
||||||
ScaleTargetRef ScaleTargetRef `json:"scaleTargetRef,omitempty"`
|
|
||||||
|
|
||||||
// MinReplicas is the minimum number of replicas the deployment is allowed to scale
|
|
||||||
// +optional
|
|
||||||
MinReplicas *int `json:"minReplicas,omitempty"`
|
|
||||||
|
|
||||||
// MaxReplicas is the maximum number of replicas the deployment is allowed to scale
|
|
||||||
// +optional
|
|
||||||
MaxReplicas *int `json:"maxReplicas,omitempty"`
|
|
||||||
|
|
||||||
// ScaleDownDelaySecondsAfterScaleUp is the approximate delay for a scale down followed by a scale up
|
|
||||||
// Used to prevent flapping (down->up->down->... loop)
|
|
||||||
// +optional
|
|
||||||
ScaleDownDelaySecondsAfterScaleUp *int `json:"scaleDownDelaySecondsAfterScaleOut,omitempty"`
|
|
||||||
|
|
||||||
// Metrics is the collection of various metric targets to calculate desired number of runners
|
|
||||||
// +optional
|
|
||||||
Metrics []MetricSpec `json:"metrics,omitempty"`
|
|
||||||
|
|
||||||
// ScaleUpTriggers is an experimental feature to increase the desired replicas by 1
|
|
||||||
// on each webhook requested received by the webhookBasedAutoscaler.
|
|
||||||
//
|
|
||||||
// This feature requires you to also enable and deploy the webhookBasedAutoscaler onto your cluster.
|
|
||||||
//
|
|
||||||
// Note that the added runners remain until the next sync period at least,
|
|
||||||
// and they may or may not be used by GitHub Actions depending on the timing.
|
|
||||||
// They are intended to be used to gain "resource slack" immediately after you
|
|
||||||
// receive a webhook from GitHub, so that you can loosely expect MinReplicas runners to be always available.
|
|
||||||
ScaleUpTriggers []ScaleUpTrigger `json:"scaleUpTriggers,omitempty"`
|
|
||||||
|
|
||||||
CapacityReservations []CapacityReservation `json:"capacityReservations,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
|
|
||||||
|
|
||||||
// ScheduledOverrides is the list of ScheduledOverride.
|
|
||||||
// It can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule.
|
|
||||||
// The earlier a scheduled override is, the higher it is prioritized.
|
|
||||||
// +optional
|
|
||||||
ScheduledOverrides []ScheduledOverride `json:"scheduledOverrides,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
GitHubAPICredentialsFrom *GitHubAPICredentialsFrom `json:"githubAPICredentialsFrom,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ScaleUpTrigger struct {
|
|
||||||
GitHubEvent *GitHubEventScaleUpTriggerSpec `json:"githubEvent,omitempty"`
|
|
||||||
Amount int `json:"amount,omitempty"`
|
|
||||||
Duration metav1.Duration `json:"duration,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GitHubEventScaleUpTriggerSpec struct {
|
|
||||||
CheckRun *CheckRunSpec `json:"checkRun,omitempty"`
|
|
||||||
PullRequest *PullRequestSpec `json:"pullRequest,omitempty"`
|
|
||||||
Push *PushSpec `json:"push,omitempty"`
|
|
||||||
WorkflowJob *WorkflowJobSpec `json:"workflowJob,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.github.com/en/actions/reference/events-that-trigger-workflows#check_run
|
|
||||||
type CheckRunSpec struct {
|
|
||||||
// One of: created, rerequested, or completed
|
|
||||||
Types []string `json:"types,omitempty"`
|
|
||||||
Status string `json:"status,omitempty"`
|
|
||||||
|
|
||||||
// Names is a list of GitHub Actions glob patterns.
|
|
||||||
// Any check_run event whose name matches one of patterns in the list can trigger autoscaling.
|
|
||||||
// Note that check_run name seem to equal to the job name you've defined in your actions workflow yaml file.
|
|
||||||
// So it is very likely that you can utilize this to trigger depending on the job.
|
|
||||||
Names []string `json:"names,omitempty"`
|
|
||||||
|
|
||||||
// Repositories is a list of GitHub repositories.
|
|
||||||
// Any check_run event whose repository matches one of repositories in the list can trigger autoscaling.
|
|
||||||
Repositories []string `json:"repositories,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_job
|
|
||||||
type WorkflowJobSpec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request
|
|
||||||
type PullRequestSpec struct {
|
|
||||||
Types []string `json:"types,omitempty"`
|
|
||||||
Branches []string `json:"branches,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushSpec is the condition for triggering scale-up on push event
|
|
||||||
// Also see https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push
|
|
||||||
type PushSpec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// CapacityReservation specifies the number of replicas temporarily added
|
|
||||||
// to the scale target until ExpirationTime.
|
|
||||||
type CapacityReservation struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
ExpirationTime metav1.Time `json:"expirationTime,omitempty"`
|
|
||||||
Replicas int `json:"replicas,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
EffectiveTime metav1.Time `json:"effectiveTime,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ScaleTargetRef struct {
|
|
||||||
// Kind is the type of resource being referenced
|
|
||||||
// +optional
|
|
||||||
// +kubebuilder:validation:Enum=RunnerDeployment;RunnerSet
|
|
||||||
Kind string `json:"kind,omitempty"`
|
|
||||||
|
|
||||||
// Name is the name of resource being referenced
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MetricSpec struct {
|
|
||||||
// Type is the type of metric to be used for autoscaling.
|
|
||||||
// It can be TotalNumberOfQueuedAndInProgressWorkflowRuns or PercentageRunnersBusy.
|
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
|
|
||||||
// RepositoryNames is the list of repository names to be used for calculating the metric.
|
|
||||||
// For example, a repository name is the REPO part of `github.com/USER/REPO`.
|
|
||||||
// +optional
|
|
||||||
RepositoryNames []string `json:"repositoryNames,omitempty"`
|
|
||||||
|
|
||||||
// ScaleUpThreshold is the percentage of busy runners greater than which will
|
|
||||||
// trigger the hpa to scale runners up.
|
|
||||||
// +optional
|
|
||||||
ScaleUpThreshold string `json:"scaleUpThreshold,omitempty"`
|
|
||||||
|
|
||||||
// ScaleDownThreshold is the percentage of busy runners less than which will
|
|
||||||
// trigger the hpa to scale the runners down.
|
|
||||||
// +optional
|
|
||||||
ScaleDownThreshold string `json:"scaleDownThreshold,omitempty"`
|
|
||||||
|
|
||||||
// ScaleUpFactor is the multiplicative factor applied to the current number of runners used
|
|
||||||
// to determine how many pods should be added.
|
|
||||||
// +optional
|
|
||||||
ScaleUpFactor string `json:"scaleUpFactor,omitempty"`
|
|
||||||
|
|
||||||
// ScaleDownFactor is the multiplicative factor applied to the current number of runners used
|
|
||||||
// to determine how many pods should be removed.
|
|
||||||
// +optional
|
|
||||||
ScaleDownFactor string `json:"scaleDownFactor,omitempty"`
|
|
||||||
|
|
||||||
// ScaleUpAdjustment is the number of runners added on scale-up.
|
|
||||||
// You can only specify either ScaleUpFactor or ScaleUpAdjustment.
|
|
||||||
// +optional
|
|
||||||
ScaleUpAdjustment int `json:"scaleUpAdjustment,omitempty"`
|
|
||||||
|
|
||||||
// ScaleDownAdjustment is the number of runners removed on scale-down.
|
|
||||||
// You can only specify either ScaleDownFactor or ScaleDownAdjustment.
|
|
||||||
// +optional
|
|
||||||
ScaleDownAdjustment int `json:"scaleDownAdjustment,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScheduledOverride can be used to override a few fields of HorizontalRunnerAutoscalerSpec on schedule.
|
|
||||||
// A schedule can optionally be recurring, so that the corresponding override happens every day, week, month, or year.
|
|
||||||
type ScheduledOverride struct {
|
|
||||||
// StartTime is the time at which the first override starts.
|
|
||||||
StartTime metav1.Time `json:"startTime"`
|
|
||||||
|
|
||||||
// EndTime is the time at which the first override ends.
|
|
||||||
EndTime metav1.Time `json:"endTime"`
|
|
||||||
|
|
||||||
// MinReplicas is the number of runners while overriding.
|
|
||||||
// If omitted, it doesn't override minReplicas.
|
|
||||||
// +optional
|
|
||||||
// +nullable
|
|
||||||
// +kubebuilder:validation:Minimum=0
|
|
||||||
MinReplicas *int `json:"minReplicas,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
RecurrenceRule RecurrenceRule `json:"recurrenceRule,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RecurrenceRule struct {
|
|
||||||
// Frequency is the name of a predefined interval of each recurrence.
|
|
||||||
// The valid values are "Daily", "Weekly", "Monthly", and "Yearly".
|
|
||||||
// If empty, the corresponding override happens only once.
|
|
||||||
// +optional
|
|
||||||
// +kubebuilder:validation:Enum=Daily;Weekly;Monthly;Yearly
|
|
||||||
Frequency string `json:"frequency,omitempty"`
|
|
||||||
|
|
||||||
// UntilTime is the time of the final recurrence.
|
|
||||||
// If empty, the schedule recurs forever.
|
|
||||||
// +optional
|
|
||||||
UntilTime metav1.Time `json:"untilTime,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HorizontalRunnerAutoscalerStatus struct {
|
|
||||||
// ObservedGeneration is the most recent generation observed for the target. It corresponds to e.g.
|
|
||||||
// RunnerDeployment's generation, which is updated on mutation by the API Server.
|
|
||||||
// +optional
|
|
||||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
|
||||||
|
|
||||||
// DesiredReplicas is the total number of desired, non-terminated and latest pods to be set for the primary RunnerSet
|
|
||||||
// This doesn't include outdated pods while upgrading the deployment and replacing the runnerset.
|
|
||||||
// +optional
|
|
||||||
DesiredReplicas *int `json:"desiredReplicas,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
// +nullable
|
|
||||||
LastSuccessfulScaleOutTime *metav1.Time `json:"lastSuccessfulScaleOutTime,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
CacheEntries []CacheEntry `json:"cacheEntries,omitempty"`
|
|
||||||
|
|
||||||
// ScheduledOverridesSummary is the summary of active and upcoming scheduled overrides to be shown in e.g. a column of a `kubectl get hra` output
|
|
||||||
// for observability.
|
|
||||||
// +optional
|
|
||||||
ScheduledOverridesSummary *string `json:"scheduledOverridesSummary,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
const CacheEntryKeyDesiredReplicas = "desiredReplicas"
|
|
||||||
|
|
||||||
type CacheEntry struct {
|
|
||||||
Key string `json:"key,omitempty"`
|
|
||||||
Value int `json:"value,omitempty"`
|
|
||||||
ExpirationTime metav1.Time `json:"expirationTime,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
// +kubebuilder:resource:shortName=hra
|
|
||||||
// +kubebuilder:subresource:status
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.minReplicas",name=Min,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.maxReplicas",name=Max,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.desiredReplicas",name=Desired,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.scheduledOverridesSummary",name=Schedule,type=string
|
|
||||||
|
|
||||||
// HorizontalRunnerAutoscaler is the Schema for the horizontalrunnerautoscaler API
|
|
||||||
type HorizontalRunnerAutoscaler struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
||||||
|
|
||||||
Spec HorizontalRunnerAutoscalerSpec `json:"spec,omitempty"`
|
|
||||||
Status HorizontalRunnerAutoscalerStatus `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
|
|
||||||
// HorizontalRunnerAutoscalerList contains a list of HorizontalRunnerAutoscaler
|
|
||||||
type HorizontalRunnerAutoscalerList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
|
||||||
Items []HorizontalRunnerAutoscaler `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SchemeBuilder.Register(&HorizontalRunnerAutoscaler{}, &HorizontalRunnerAutoscalerList{})
|
|
||||||
}
|
|
||||||
|
|
@ -1,408 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RunnerSpec defines the desired state of Runner
|
|
||||||
type RunnerSpec struct {
|
|
||||||
RunnerConfig `json:",inline"`
|
|
||||||
RunnerPodSpec `json:",inline"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RunnerConfig struct {
|
|
||||||
// +optional
|
|
||||||
// +kubebuilder:validation:Pattern=`^[^/]+$`
|
|
||||||
Enterprise string `json:"enterprise,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
// +kubebuilder:validation:Pattern=`^[^/]+$`
|
|
||||||
Organization string `json:"organization,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
// +kubebuilder:validation:Pattern=`^[^/]+/[^/]+$`
|
|
||||||
Repository string `json:"repository,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Labels []string `json:"labels,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Group string `json:"group,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Ephemeral *bool `json:"ephemeral,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Image string `json:"image"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
WorkDir string `json:"workDir,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
DockerdWithinRunnerContainer *bool `json:"dockerdWithinRunnerContainer,omitempty"`
|
|
||||||
// +optional
|
|
||||||
DockerEnabled *bool `json:"dockerEnabled,omitempty"`
|
|
||||||
// +optional
|
|
||||||
DockerMTU *int64 `json:"dockerMTU,omitempty"`
|
|
||||||
// +optional
|
|
||||||
DockerRegistryMirror *string `json:"dockerRegistryMirror,omitempty"`
|
|
||||||
// +optional
|
|
||||||
DockerVarRunVolumeSizeLimit *resource.Quantity `json:"dockerVarRunVolumeSizeLimit,omitempty"`
|
|
||||||
// +optional
|
|
||||||
VolumeSizeLimit *resource.Quantity `json:"volumeSizeLimit,omitempty"`
|
|
||||||
// +optional
|
|
||||||
VolumeStorageMedium *string `json:"volumeStorageMedium,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
ContainerMode string `json:"containerMode,omitempty"`
|
|
||||||
|
|
||||||
GitHubAPICredentialsFrom *GitHubAPICredentialsFrom `json:"githubAPICredentialsFrom,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GitHubAPICredentialsFrom struct {
|
|
||||||
SecretRef SecretReference `json:"secretRef,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SecretReference struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunnerPodSpec defines the desired pod spec fields of the runner pod
|
|
||||||
type RunnerPodSpec struct {
|
|
||||||
// +optional
|
|
||||||
DockerdContainerResources corev1.ResourceRequirements `json:"dockerdContainerResources,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
DockerVolumeMounts []corev1.VolumeMount `json:"dockerVolumeMounts,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
DockerEnv []corev1.EnvVar `json:"dockerEnv,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Containers []corev1.Container `json:"containers,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Env []corev1.EnvVar `json:"env,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Volumes []corev1.Volume `json:"volumes,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
EnableServiceLinks *bool `json:"enableServiceLinks,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
InitContainers []corev1.Container `json:"initContainers,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
ServiceAccountName string `json:"serviceAccountName,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
AutomountServiceAccountToken *bool `json:"automountServiceAccountToken,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
SidecarContainers []corev1.Container `json:"sidecarContainers,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Affinity *corev1.Affinity `json:"affinity,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
PriorityClassName string `json:"priorityClassName,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
EphemeralContainers []corev1.EphemeralContainer `json:"ephemeralContainers,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
HostAliases []corev1.HostAlias `json:"hostAliases,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"`
|
|
||||||
|
|
||||||
// RuntimeClassName is the container runtime configuration that containers should run under.
|
|
||||||
// More info: https://kubernetes.io/docs/concepts/containers/runtime-class
|
|
||||||
// +optional
|
|
||||||
RuntimeClassName *string `json:"runtimeClassName,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
DnsPolicy corev1.DNSPolicy `json:"dnsPolicy,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
DnsConfig *corev1.PodDNSConfig `json:"dnsConfig,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
WorkVolumeClaimTemplate *WorkVolumeClaimTemplate `json:"workVolumeClaimTemplate,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *RunnerSpec) Validate(rootPath *field.Path) field.ErrorList {
|
|
||||||
var (
|
|
||||||
errList field.ErrorList
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
err = rs.validateRepository()
|
|
||||||
if err != nil {
|
|
||||||
errList = append(errList, field.Invalid(rootPath.Child("repository"), rs.Repository, err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = rs.validateWorkVolumeClaimTemplate()
|
|
||||||
if err != nil {
|
|
||||||
errList = append(errList, field.Invalid(rootPath.Child("workVolumeClaimTemplate"), rs.WorkVolumeClaimTemplate, err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return errList
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateRepository validates repository field.
|
|
||||||
func (rs *RunnerSpec) validateRepository() error {
|
|
||||||
// Enterprise, Organization and repository are both exclusive.
|
|
||||||
foundCount := 0
|
|
||||||
if len(rs.Organization) > 0 {
|
|
||||||
foundCount += 1
|
|
||||||
}
|
|
||||||
if len(rs.Repository) > 0 {
|
|
||||||
foundCount += 1
|
|
||||||
}
|
|
||||||
if len(rs.Enterprise) > 0 {
|
|
||||||
foundCount += 1
|
|
||||||
}
|
|
||||||
if foundCount == 0 {
|
|
||||||
return errors.New("spec needs enterprise, organization or repository")
|
|
||||||
}
|
|
||||||
if foundCount > 1 {
|
|
||||||
return errors.New("spec cannot have many fields defined enterprise, organization and repository")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *RunnerSpec) validateWorkVolumeClaimTemplate() error {
|
|
||||||
if rs.ContainerMode != "kubernetes" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if rs.WorkVolumeClaimTemplate == nil {
|
|
||||||
return errors.New("Spec.ContainerMode: kubernetes must have workVolumeClaimTemplate field specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
return rs.WorkVolumeClaimTemplate.validate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunnerStatus defines the observed state of Runner
|
|
||||||
type RunnerStatus struct {
|
|
||||||
// Turns true only if the runner pod is ready.
|
|
||||||
// +optional
|
|
||||||
Ready bool `json:"ready"`
|
|
||||||
// +optional
|
|
||||||
Registration RunnerStatusRegistration `json:"registration"`
|
|
||||||
// +optional
|
|
||||||
Phase string `json:"phase,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Reason string `json:"reason,omitempty"`
|
|
||||||
// +optional
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
// +optional
|
|
||||||
WorkflowStatus *WorkflowStatus `json:"workflow"`
|
|
||||||
// +optional
|
|
||||||
// +nullable
|
|
||||||
LastRegistrationCheckTime *metav1.Time `json:"lastRegistrationCheckTime,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WorkflowStatus contains various information that is propagated
|
|
||||||
// from GitHub Actions workflow run environment variables to
|
|
||||||
// ease monitoring workflow run/job/steps that are triggerred on the runner.
|
|
||||||
type WorkflowStatus struct {
|
|
||||||
// +optional
|
|
||||||
// Name is the name of the workflow
|
|
||||||
// that is triggerred within the runner.
|
|
||||||
// It corresponds to GITHUB_WORKFLOW defined in
|
|
||||||
// https://docs.github.com/en/actions/learn-github-actions/environment-variables
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
// +optional
|
|
||||||
// Repository is the owner and repository name of the workflow
|
|
||||||
// that is triggerred within the runner.
|
|
||||||
// It corresponds to GITHUB_REPOSITORY defined in
|
|
||||||
// https://docs.github.com/en/actions/learn-github-actions/environment-variables
|
|
||||||
Repository string `json:"repository,omitempty"`
|
|
||||||
// +optional
|
|
||||||
// ReositoryOwner is the repository owner's name for the workflow
|
|
||||||
// that is triggerred within the runner.
|
|
||||||
// It corresponds to GITHUB_REPOSITORY_OWNER defined in
|
|
||||||
// https://docs.github.com/en/actions/learn-github-actions/environment-variables
|
|
||||||
RepositoryOwner string `json:"repositoryOwner,omitempty"`
|
|
||||||
// +optional
|
|
||||||
// GITHUB_RUN_NUMBER is the unique number for the current workflow run
|
|
||||||
// that is triggerred within the runner.
|
|
||||||
// It corresponds to GITHUB_RUN_ID defined in
|
|
||||||
// https://docs.github.com/en/actions/learn-github-actions/environment-variables
|
|
||||||
RunNumber string `json:"runNumber,omitempty"`
|
|
||||||
// +optional
|
|
||||||
// RunID is the unique number for the current workflow run
|
|
||||||
// that is triggerred within the runner.
|
|
||||||
// It corresponds to GITHUB_RUN_ID defined in
|
|
||||||
// https://docs.github.com/en/actions/learn-github-actions/environment-variables
|
|
||||||
RunID string `json:"runID,omitempty"`
|
|
||||||
// +optional
|
|
||||||
// Job is the name of the current job
|
|
||||||
// that is triggerred within the runner.
|
|
||||||
// It corresponds to GITHUB_JOB defined in
|
|
||||||
// https://docs.github.com/en/actions/learn-github-actions/environment-variables
|
|
||||||
Job string `json:"job,omitempty"`
|
|
||||||
// +optional
|
|
||||||
// Action is the name of the current action or the step ID of the current step
|
|
||||||
// that is triggerred within the runner.
|
|
||||||
// It corresponds to GITHUB_ACTION defined in
|
|
||||||
// https://docs.github.com/en/actions/learn-github-actions/environment-variables
|
|
||||||
Action string `json:"action,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunnerStatusRegistration contains runner registration status
|
|
||||||
type RunnerStatusRegistration struct {
|
|
||||||
Enterprise string `json:"enterprise,omitempty"`
|
|
||||||
Organization string `json:"organization,omitempty"`
|
|
||||||
Repository string `json:"repository,omitempty"`
|
|
||||||
Labels []string `json:"labels,omitempty"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
ExpiresAt metav1.Time `json:"expiresAt"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type WorkVolumeClaimTemplate struct {
|
|
||||||
StorageClassName string `json:"storageClassName"`
|
|
||||||
AccessModes []corev1.PersistentVolumeAccessMode `json:"accessModes"`
|
|
||||||
Resources corev1.VolumeResourceRequirements `json:"resources"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WorkVolumeClaimTemplate) validate() error {
|
|
||||||
if len(w.AccessModes) == 0 {
|
|
||||||
return errors.New("access mode should have at least one mode specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, accessMode := range w.AccessModes {
|
|
||||||
switch accessMode {
|
|
||||||
case corev1.ReadWriteOnce, corev1.ReadWriteMany:
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("access mode %v is not supported", accessMode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WorkVolumeClaimTemplate) V1Volume() corev1.Volume {
|
|
||||||
return corev1.Volume{
|
|
||||||
Name: "work",
|
|
||||||
VolumeSource: corev1.VolumeSource{
|
|
||||||
Ephemeral: &corev1.EphemeralVolumeSource{
|
|
||||||
VolumeClaimTemplate: &corev1.PersistentVolumeClaimTemplate{
|
|
||||||
Spec: corev1.PersistentVolumeClaimSpec{
|
|
||||||
AccessModes: w.AccessModes,
|
|
||||||
StorageClassName: &w.StorageClassName,
|
|
||||||
Resources: w.Resources,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WorkVolumeClaimTemplate) V1VolumeMount(mountPath string) corev1.VolumeMount {
|
|
||||||
return corev1.VolumeMount{
|
|
||||||
MountPath: mountPath,
|
|
||||||
Name: "work",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
// +kubebuilder:subresource:status
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.enterprise",name=Enterprise,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.organization",name=Organization,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.repository",name=Repository,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.group",name=Group,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.labels",name=Labels,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Status,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.message",name=Message,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.workflow.repository",name=WF Repo,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.workflow.runID",name=WF Run,type=string
|
|
||||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
|
||||||
|
|
||||||
// Runner is the Schema for the runners API
|
|
||||||
type Runner struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
||||||
|
|
||||||
Spec RunnerSpec `json:"spec,omitempty"`
|
|
||||||
Status RunnerStatus `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Runner) IsRegisterable() bool {
|
|
||||||
if r.Status.Registration.Repository != r.Spec.Repository {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Status.Registration.Token == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
now := metav1.Now()
|
|
||||||
return !r.Status.Registration.ExpiresAt.Before(&now)
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
|
|
||||||
// RunnerList contains a list of Runner
|
|
||||||
type RunnerList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
|
||||||
Items []Runner `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SchemeBuilder.Register(&Runner{}, &RunnerList{})
|
|
||||||
}
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
|
||||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
|
||||||
)
|
|
||||||
|
|
||||||
// log is for logging in this package.
|
|
||||||
var runnerLog = logf.Log.WithName("runner-resource")
|
|
||||||
|
|
||||||
func (r *Runner) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
|
||||||
return ctrl.NewWebhookManagedBy(mgr).
|
|
||||||
For(r).
|
|
||||||
WithDefaulter(&RunnerDefaulter{}).
|
|
||||||
WithValidator(&RunnerValidator{}).
|
|
||||||
Complete()
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/mutate-actions-summerwind-dev-v1alpha1-runner,verbs=create;update,mutating=true,failurePolicy=fail,groups=actions.summerwind.dev,resources=runners,versions=v1alpha1,name=mutate.runner.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
|
||||||
|
|
||||||
var _ webhook.CustomDefaulter = &RunnerDefaulter{}
|
|
||||||
|
|
||||||
type RunnerDefaulter struct{}
|
|
||||||
|
|
||||||
// Default implements webhook.Defaulter so a webhook will be registered for the type
|
|
||||||
func (*RunnerDefaulter) Default(ctx context.Context, obj runtime.Object) error {
|
|
||||||
// Nothing to do.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/validate-actions-summerwind-dev-v1alpha1-runner,verbs=create;update,mutating=false,failurePolicy=fail,groups=actions.summerwind.dev,resources=runners,versions=v1alpha1,name=validate.runner.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
|
||||||
|
|
||||||
var _ webhook.CustomValidator = &RunnerValidator{}
|
|
||||||
|
|
||||||
type RunnerValidator struct{}
|
|
||||||
|
|
||||||
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
|
|
||||||
func (*RunnerValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
|
||||||
r, ok := obj.(*Runner)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("expected Runner object, got %T", obj)
|
|
||||||
}
|
|
||||||
runnerLog.Info("validate resource to be created", "name", r.Name)
|
|
||||||
return nil, r.Validate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
|
|
||||||
func (*RunnerValidator) ValidateUpdate(ctx context.Context, old, obj runtime.Object) (admission.Warnings, error) {
|
|
||||||
r, ok := obj.(*Runner)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("expected Runner object, got %T", obj)
|
|
||||||
}
|
|
||||||
runnerLog.Info("validate resource to be updated", "name", r.Name)
|
|
||||||
return nil, r.Validate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
|
|
||||||
func (*RunnerValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates resource spec.
|
|
||||||
func (r *Runner) Validate() error {
|
|
||||||
errList := r.Spec.Validate(field.NewPath("spec"))
|
|
||||||
|
|
||||||
if len(errList) > 0 {
|
|
||||||
return apierrors.NewInvalid(r.GroupVersionKind().GroupKind(), r.Name, errList)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
AutoscalingMetricTypeTotalNumberOfQueuedAndInProgressWorkflowRuns = "TotalNumberOfQueuedAndInProgressWorkflowRuns"
|
|
||||||
AutoscalingMetricTypePercentageRunnersBusy = "PercentageRunnersBusy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RunnerDeploymentSpec defines the desired state of RunnerDeployment
|
|
||||||
type RunnerDeploymentSpec struct {
|
|
||||||
// +optional
|
|
||||||
// +nullable
|
|
||||||
Replicas *int `json:"replicas,omitempty"`
|
|
||||||
|
|
||||||
// EffectiveTime is the time the upstream controller requested to sync Replicas.
|
|
||||||
// It is usually populated by the webhook-based autoscaler via HRA.
|
|
||||||
// The value is inherited to RunnerReplicaSet(s) and used to prevent ephemeral runners from unnecessarily recreated.
|
|
||||||
//
|
|
||||||
// +optional
|
|
||||||
// +nullable
|
|
||||||
EffectiveTime *metav1.Time `json:"effectiveTime"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
// +nullable
|
|
||||||
Selector *metav1.LabelSelector `json:"selector"`
|
|
||||||
Template RunnerTemplate `json:"template"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RunnerDeploymentStatus struct {
|
|
||||||
// See K8s deployment controller code for reference
|
|
||||||
// https://github.com/kubernetes/kubernetes/blob/ea0764452222146c47ec826977f49d7001b0ea8c/pkg/controller/deployment/sync.go#L487-L505
|
|
||||||
|
|
||||||
// AvailableReplicas is the total number of available runners which have been successfully registered to GitHub and still running.
|
|
||||||
// This corresponds to the sum of status.availableReplicas of all the runner replica sets.
|
|
||||||
// +optional
|
|
||||||
AvailableReplicas *int `json:"availableReplicas"`
|
|
||||||
|
|
||||||
// ReadyReplicas is the total number of available runners which have been successfully registered to GitHub and still running.
|
|
||||||
// This corresponds to the sum of status.readyReplicas of all the runner replica sets.
|
|
||||||
// +optional
|
|
||||||
ReadyReplicas *int `json:"readyReplicas"`
|
|
||||||
|
|
||||||
// ReadyReplicas is the total number of available runners which have been successfully registered to GitHub and still running.
|
|
||||||
// This corresponds to status.replicas of the runner replica set that has the desired template hash.
|
|
||||||
// +optional
|
|
||||||
UpdatedReplicas *int `json:"updatedReplicas"`
|
|
||||||
|
|
||||||
// DesiredReplicas is the total number of desired, non-terminated and latest pods to be set for the primary RunnerSet
|
|
||||||
// This doesn't include outdated pods while upgrading the deployment and replacing the runnerset.
|
|
||||||
// +optional
|
|
||||||
DesiredReplicas *int `json:"desiredReplicas"`
|
|
||||||
|
|
||||||
// Replicas is the total number of replicas
|
|
||||||
// +optional
|
|
||||||
Replicas *int `json:"replicas"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
// +kubebuilder:resource:shortName=rdeploy
|
|
||||||
// +kubebuilder:subresource:status
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.template.spec.enterprise",name=Enterprise,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.template.spec.organization",name=Organization,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.template.spec.repository",name=Repository,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.template.spec.group",name=Group,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.template.spec.labels",name=Labels,type=string
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name=Desired,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Current,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.updatedReplicas",name=Up-To-Date,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.availableReplicas",name=Available,type=number
|
|
||||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
|
||||||
|
|
||||||
// RunnerDeployment is the Schema for the runnerdeployments API
|
|
||||||
type RunnerDeployment struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
||||||
|
|
||||||
Spec RunnerDeploymentSpec `json:"spec,omitempty"`
|
|
||||||
Status RunnerDeploymentStatus `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
|
|
||||||
// RunnerList contains a list of Runner
|
|
||||||
type RunnerDeploymentList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
|
||||||
Items []RunnerDeployment `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SchemeBuilder.Register(&RunnerDeployment{}, &RunnerDeploymentList{})
|
|
||||||
}
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
|
||||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
|
||||||
)
|
|
||||||
|
|
||||||
// log is for logging in this package.
|
|
||||||
var runnerDeploymentLog = logf.Log.WithName("runnerdeployment-resource")
|
|
||||||
|
|
||||||
func (r *RunnerDeployment) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
|
||||||
return ctrl.NewWebhookManagedBy(mgr).
|
|
||||||
For(r).
|
|
||||||
WithDefaulter(&RunnerDeploymentDefaulter{}).
|
|
||||||
WithValidator(&RunnerDeploymentValidator{}).
|
|
||||||
Complete()
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/mutate-actions-summerwind-dev-v1alpha1-runnerdeployment,verbs=create;update,mutating=true,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerdeployments,versions=v1alpha1,name=mutate.runnerdeployment.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
|
||||||
|
|
||||||
var _ webhook.CustomDefaulter = &RunnerDeploymentDefaulter{}
|
|
||||||
|
|
||||||
type RunnerDeploymentDefaulter struct{}
|
|
||||||
|
|
||||||
// Default implements webhook.Defaulter so a webhook will be registered for the type
|
|
||||||
func (*RunnerDeploymentDefaulter) Default(context.Context, runtime.Object) error {
|
|
||||||
// Nothing to do.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/validate-actions-summerwind-dev-v1alpha1-runnerdeployment,verbs=create;update,mutating=false,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerdeployments,versions=v1alpha1,name=validate.runnerdeployment.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
|
||||||
|
|
||||||
var _ webhook.CustomValidator = &RunnerDeploymentValidator{}
|
|
||||||
|
|
||||||
type RunnerDeploymentValidator struct{}
|
|
||||||
|
|
||||||
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
|
|
||||||
func (*RunnerDeploymentValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
|
||||||
r, ok := obj.(*RunnerDeployment)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("expected RunnerDeployment object, got %T", obj)
|
|
||||||
}
|
|
||||||
runnerDeploymentLog.Info("validate resource to be created", "name", r.Name)
|
|
||||||
return nil, r.Validate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
|
|
||||||
func (*RunnerDeploymentValidator) ValidateUpdate(ctx context.Context, old, obj runtime.Object) (admission.Warnings, error) {
|
|
||||||
r, ok := obj.(*RunnerDeployment)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("expected RunnerDeployment object, got %T", obj)
|
|
||||||
}
|
|
||||||
runnerDeploymentLog.Info("validate resource to be updated", "name", r.Name)
|
|
||||||
return nil, r.Validate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
|
|
||||||
func (*RunnerDeploymentValidator) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates resource spec.
|
|
||||||
func (r *RunnerDeployment) Validate() error {
|
|
||||||
errList := r.Spec.Template.Spec.Validate(field.NewPath("spec", "template", "spec"))
|
|
||||||
|
|
||||||
if len(errList) > 0 {
|
|
||||||
return apierrors.NewInvalid(r.GroupVersionKind().GroupKind(), r.Name, errList)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RunnerReplicaSetSpec defines the desired state of RunnerReplicaSet
|
|
||||||
type RunnerReplicaSetSpec struct {
|
|
||||||
// +optional
|
|
||||||
// +nullable
|
|
||||||
Replicas *int `json:"replicas,omitempty"`
|
|
||||||
|
|
||||||
// EffectiveTime is the time the upstream controller requested to sync Replicas.
|
|
||||||
// It is usually populated by the webhook-based autoscaler via HRA and RunnerDeployment.
|
|
||||||
// The value is used to prevent runnerreplicaset controller from unnecessarily recreating ephemeral runners
|
|
||||||
// based on potentially outdated Replicas value.
|
|
||||||
//
|
|
||||||
// +optional
|
|
||||||
// +nullable
|
|
||||||
EffectiveTime *metav1.Time `json:"effectiveTime"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
// +nullable
|
|
||||||
Selector *metav1.LabelSelector `json:"selector"`
|
|
||||||
Template RunnerTemplate `json:"template"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RunnerReplicaSetStatus struct {
|
|
||||||
// See K8s replicaset controller code for reference
|
|
||||||
// https://github.com/kubernetes/kubernetes/blob/ea0764452222146c47ec826977f49d7001b0ea8c/pkg/controller/replicaset/replica_set_utils.go#L101-L106
|
|
||||||
|
|
||||||
// Replicas is the number of runners that are created and still being managed by this runner replica set.
|
|
||||||
// +optional
|
|
||||||
Replicas *int `json:"replicas"`
|
|
||||||
|
|
||||||
// ReadyReplicas is the number of runners that are created and Runnning.
|
|
||||||
ReadyReplicas *int `json:"readyReplicas"`
|
|
||||||
|
|
||||||
// AvailableReplicas is the number of runners that are created and Runnning.
|
|
||||||
// This is currently same as ReadyReplicas but perserved for future use.
|
|
||||||
AvailableReplicas *int `json:"availableReplicas"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RunnerTemplate struct {
|
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
||||||
|
|
||||||
Spec RunnerSpec `json:"spec,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
// +kubebuilder:resource:shortName=rrs
|
|
||||||
// +kubebuilder:subresource:status
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name=Desired,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Current,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.readyReplicas",name=Ready,type=number
|
|
||||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
|
||||||
|
|
||||||
// RunnerReplicaSet is the Schema for the runnerreplicasets API
|
|
||||||
type RunnerReplicaSet struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
||||||
|
|
||||||
Spec RunnerReplicaSetSpec `json:"spec,omitempty"`
|
|
||||||
Status RunnerReplicaSetStatus `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
|
|
||||||
// RunnerList contains a list of Runner
|
|
||||||
type RunnerReplicaSetList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
|
||||||
Items []RunnerReplicaSet `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SchemeBuilder.Register(&RunnerReplicaSet{}, &RunnerReplicaSetList{})
|
|
||||||
}
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
|
||||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
|
||||||
)
|
|
||||||
|
|
||||||
// log is for logging in this package.
|
|
||||||
var runnerReplicaSetLog = logf.Log.WithName("runnerreplicaset-resource")
|
|
||||||
|
|
||||||
func (r *RunnerReplicaSet) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
|
||||||
return ctrl.NewWebhookManagedBy(mgr).
|
|
||||||
For(r).
|
|
||||||
WithDefaulter(&RunnerReplicaSetDefaulter{}).
|
|
||||||
WithValidator(&RunnerReplicaSetValidator{}).
|
|
||||||
Complete()
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/mutate-actions-summerwind-dev-v1alpha1-runnerreplicaset,verbs=create;update,mutating=true,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerreplicasets,versions=v1alpha1,name=mutate.runnerreplicaset.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
|
||||||
|
|
||||||
var _ webhook.CustomDefaulter = &RunnerReplicaSetDefaulter{}
|
|
||||||
|
|
||||||
type RunnerReplicaSetDefaulter struct{}
|
|
||||||
|
|
||||||
// Default implements webhook.Defaulter so a webhook will be registered for the type
|
|
||||||
func (*RunnerReplicaSetDefaulter) Default(context.Context, runtime.Object) error {
|
|
||||||
// Nothing to do.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:webhook:path=/validate-actions-summerwind-dev-v1alpha1-runnerreplicaset,verbs=create;update,mutating=false,failurePolicy=fail,groups=actions.summerwind.dev,resources=runnerreplicasets,versions=v1alpha1,name=validate.runnerreplicaset.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
|
|
||||||
|
|
||||||
var _ webhook.CustomValidator = &RunnerReplicaSetValidator{}
|
|
||||||
|
|
||||||
type RunnerReplicaSetValidator struct{}
|
|
||||||
|
|
||||||
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
|
|
||||||
func (*RunnerReplicaSetValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
|
||||||
r, ok := obj.(*RunnerReplicaSet)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("expected RunnerReplicaSet object, got %T", obj)
|
|
||||||
}
|
|
||||||
runnerReplicaSetLog.Info("validate resource to be created", "name", r.Name)
|
|
||||||
return nil, r.Validate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
|
|
||||||
func (*RunnerReplicaSetValidator) ValidateUpdate(ctx context.Context, old, obj runtime.Object) (admission.Warnings, error) {
|
|
||||||
r, ok := obj.(*RunnerReplicaSet)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("expected RunnerReplicaSet object, got %T", obj)
|
|
||||||
}
|
|
||||||
runnerReplicaSetLog.Info("validate resource to be updated", "name", r.Name)
|
|
||||||
return nil, r.Validate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
|
|
||||||
func (*RunnerReplicaSetValidator) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates resource spec.
|
|
||||||
func (r *RunnerReplicaSet) Validate() error {
|
|
||||||
errList := r.Spec.Template.Spec.Validate(field.NewPath("spec", "template", "spec"))
|
|
||||||
|
|
||||||
if len(errList) > 0 {
|
|
||||||
return apierrors.NewInvalid(r.GroupVersionKind().GroupKind(), r.Name, errList)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2021 The actions-runner-controller authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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 v1alpha1
|
|
||||||
|
|
||||||
import (
|
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RunnerSetSpec defines the desired state of RunnerSet
|
|
||||||
type RunnerSetSpec struct {
|
|
||||||
RunnerConfig `json:",inline"`
|
|
||||||
|
|
||||||
// EffectiveTime is the time the upstream controller requested to sync Replicas.
|
|
||||||
// It is usually populated by the webhook-based autoscaler via HRA.
|
|
||||||
// It is used to prevent ephemeral runners from unnecessarily recreated.
|
|
||||||
//
|
|
||||||
// +optional
|
|
||||||
// +nullable
|
|
||||||
EffectiveTime *metav1.Time `json:"effectiveTime,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
ServiceAccountName string `json:"serviceAccountName,omitempty"`
|
|
||||||
|
|
||||||
// +optional
|
|
||||||
WorkVolumeClaimTemplate *WorkVolumeClaimTemplate `json:"workVolumeClaimTemplate,omitempty"`
|
|
||||||
|
|
||||||
appsv1.StatefulSetSpec `json:",inline"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RunnerSetStatus struct {
|
|
||||||
// See K8s deployment controller code for reference
|
|
||||||
// https://github.com/kubernetes/kubernetes/blob/ea0764452222146c47ec826977f49d7001b0ea8c/pkg/controller/deployment/sync.go#L487-L505
|
|
||||||
|
|
||||||
// AvailableReplicas is the total number of available runners which have been successfully registered to GitHub and still running.
|
|
||||||
// This corresponds to the sum of status.availableReplicas of all the runner replica sets.
|
|
||||||
// +optional
|
|
||||||
CurrentReplicas *int `json:"availableReplicas"`
|
|
||||||
|
|
||||||
// ReadyReplicas is the total number of available runners which have been successfully registered to GitHub and still running.
|
|
||||||
// This corresponds to the sum of status.readyReplicas of all the runner replica sets.
|
|
||||||
// +optional
|
|
||||||
ReadyReplicas *int `json:"readyReplicas"`
|
|
||||||
|
|
||||||
// ReadyReplicas is the total number of available runners which have been successfully registered to GitHub and still running.
|
|
||||||
// This corresponds to status.replicas of the runner replica set that has the desired template hash.
|
|
||||||
// +optional
|
|
||||||
UpdatedReplicas *int `json:"updatedReplicas"`
|
|
||||||
|
|
||||||
// DesiredReplicas is the total number of desired, non-terminated and latest pods to be set for the primary RunnerSet
|
|
||||||
// This doesn't include outdated pods while upgrading the deployment and replacing the runnerset.
|
|
||||||
// +optional
|
|
||||||
DesiredReplicas *int `json:"desiredReplicas"`
|
|
||||||
|
|
||||||
// Replicas is the total number of replicas
|
|
||||||
// +optional
|
|
||||||
Replicas *int `json:"replicas"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
// +kubebuilder:subresource:status
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".spec.replicas",name=Desired,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Current,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.updatedReplicas",name=Up-To-Date,type=number
|
|
||||||
// +kubebuilder:printcolumn:JSONPath=".status.availableReplicas",name=Available,type=number
|
|
||||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
|
||||||
|
|
||||||
// RunnerSet is the Schema for the runnersets API
|
|
||||||
type RunnerSet struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
||||||
|
|
||||||
Spec RunnerSetSpec `json:"spec,omitempty"`
|
|
||||||
Status RunnerSetStatus `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
|
||||||
|
|
||||||
// RunnerList contains a list of Runner
|
|
||||||
type RunnerSetList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
|
||||||
Items []RunnerSet `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SchemeBuilder.Register(&RunnerSet{}, &RunnerSetList{})
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue