From d690c19c589ae86a8abb2b67c9acfe796dbc9f97 Mon Sep 17 00:00:00 2001 From: Mojtaba Date: Mon, 26 Aug 2024 11:26:49 +0330 Subject: [PATCH 1/2] feat: CTRL+C to run timeout handler with no sleep --- go.mod | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- pkg/k8s/configmap.go | 4 ++++ pkg/k8s/custom_resource.go | 3 +++ pkg/k8s/daemonset.go | 3 +++ pkg/k8s/deployment.go | 4 ++++ pkg/k8s/errors.go | 1 + pkg/k8s/k8s.go | 6 ++++++ pkg/k8s/namespace.go | 3 +++ pkg/k8s/networkpolicy.go | 3 +++ pkg/k8s/pod.go | 6 ++++++ pkg/k8s/pvc.go | 3 +++ pkg/k8s/replicaset.go | 6 ++++++ pkg/k8s/role.go | 6 ++++++ pkg/k8s/rolebinding.go | 6 ++++++ pkg/k8s/service.go | 9 +++++++++ pkg/k8s/serviceaccount.go | 6 ++++++ pkg/k8s/types.go | 1 + pkg/knuu/instance_old.go | 3 ++- pkg/knuu/knuu.go | 37 +++++++++++++++++++++++-------------- pkg/knuu/knuu_test.go | 4 ++-- 21 files changed, 121 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index 9643e5a3..1c51032d 100644 --- a/go.mod +++ b/go.mod @@ -75,17 +75,17 @@ require ( go.opentelemetry.io/otel/trace v1.26.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/mod v0.20.0 // indirect + golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/term v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.24.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect diff --git a/go.sum b/go.sum index 4fec4f36..247036da 100644 --- a/go.sum +++ b/go.sum @@ -210,8 +210,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= @@ -221,8 +221,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -234,8 +234,8 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= @@ -245,8 +245,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -261,19 +261,19 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -285,8 +285,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/k8s/configmap.go b/pkg/k8s/configmap.go index 091cf94c..da18a4ca 100644 --- a/pkg/k8s/configmap.go +++ b/pkg/k8s/configmap.go @@ -33,6 +33,10 @@ func (c *Client) CreateConfigMap( ctx context.Context, name string, labels, data map[string]string, ) (*v1.ConfigMap, error) { + if c.terminated { + return nil, ErrClientTerminated + } + if err := validateConfigMapName(name); err != nil { return nil, err } diff --git a/pkg/k8s/custom_resource.go b/pkg/k8s/custom_resource.go index 09047cd0..6eb78fcc 100644 --- a/pkg/k8s/custom_resource.go +++ b/pkg/k8s/custom_resource.go @@ -17,6 +17,9 @@ func (c *Client) CreateCustomResource( gvr *schema.GroupVersionResource, obj *map[string]interface{}, ) error { + if c.terminated { + return ErrClientTerminated + } if err := validateCustomResourceName(name); err != nil { return err } diff --git a/pkg/k8s/daemonset.go b/pkg/k8s/daemonset.go index f7ed8afa..48057331 100644 --- a/pkg/k8s/daemonset.go +++ b/pkg/k8s/daemonset.go @@ -35,6 +35,9 @@ func (c *Client) CreateDaemonSet( initContainers []v1.Container, containers []v1.Container, ) (*appv1.DaemonSet, error) { + if c.terminated { + return nil, ErrClientTerminated + } if err := validateDaemonSetName(name); err != nil { return nil, err } diff --git a/pkg/k8s/deployment.go b/pkg/k8s/deployment.go index ab26327f..1211435b 100644 --- a/pkg/k8s/deployment.go +++ b/pkg/k8s/deployment.go @@ -10,6 +10,10 @@ import ( func (c *Client) WaitForDeployment(ctx context.Context, name string) error { for { + if c.terminated { + return ErrClientTerminated + } + deployment, err := c.clientset.AppsV1(). Deployments(c.namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil && !errors.IsNotFound(err) { diff --git a/pkg/k8s/errors.go b/pkg/k8s/errors.go index 3bae6fb8..e1f5f57f 100644 --- a/pkg/k8s/errors.go +++ b/pkg/k8s/errors.go @@ -133,4 +133,5 @@ var ( ErrInvalidServiceAccountName = errors.New("InvalidServiceAccountName", "invalid service account name %s: %v") ErrInvalidClusterRoleBindingName = errors.New("InvalidClusterRoleBindingName", "invalid cluster role binding name %s: %v") ErrInvalidServiceName = errors.New("InvalidServiceName", "invalid service name %s: %v") + ErrClientTerminated = errors.New("ClientTerminated", "terminated by user") ) diff --git a/pkg/k8s/k8s.go b/pkg/k8s/k8s.go index a996ab11..0f40d247 100644 --- a/pkg/k8s/k8s.go +++ b/pkg/k8s/k8s.go @@ -37,6 +37,7 @@ type Client struct { dynamicClient dynamic.Interface namespace string logger *logrus.Logger + terminated bool // This flag is used to indicate that the process has been terminated by the user } var _ KubeManager = &Client{} @@ -78,6 +79,7 @@ func NewClientCustom( dynamicClient: dC, namespace: namespace, logger: logger, + terminated: false, } kc.namespace = SanitizeName(namespace) if err := kc.CreateNamespace(ctx, kc.namespace); err != nil { @@ -86,6 +88,10 @@ func NewClientCustom( return kc, nil } +func (c *Client) Terminate() { + c.terminated = true +} + func (c *Client) Clientset() kubernetes.Interface { return c.clientset } diff --git a/pkg/k8s/namespace.go b/pkg/k8s/namespace.go index 90c68e88..cad7a1d5 100644 --- a/pkg/k8s/namespace.go +++ b/pkg/k8s/namespace.go @@ -9,6 +9,9 @@ import ( ) func (c *Client) CreateNamespace(ctx context.Context, name string) error { + if c.terminated { + return ErrClientTerminated + } if err := validateNamespace(name); err != nil { return err } diff --git a/pkg/k8s/networkpolicy.go b/pkg/k8s/networkpolicy.go index d852f664..2af17596 100644 --- a/pkg/k8s/networkpolicy.go +++ b/pkg/k8s/networkpolicy.go @@ -14,6 +14,9 @@ func (c *Client) CreateNetworkPolicy( ingressSelectorMap, egressSelectorMap map[string]string, ) error { + if c.terminated { + return ErrClientTerminated + } if err := validateNetworkPolicyName(name); err != nil { return err } diff --git a/pkg/k8s/pod.go b/pkg/k8s/pod.go index 5c4c9fa1..8898a2f9 100644 --- a/pkg/k8s/pod.go +++ b/pkg/k8s/pod.go @@ -75,6 +75,9 @@ type File struct { // DeployPod creates a new pod in the namespace that k8s client is initiate with if it doesn't already exist. func (c *Client) DeployPod(ctx context.Context, podConfig PodConfig, init bool) (*v1.Pod, error) { + if c.terminated { + return nil, ErrClientTerminated + } if err := validatePodConfig(podConfig); err != nil { return nil, err } @@ -336,6 +339,9 @@ func (c *Client) PortForwardPod( } func (c *Client) getPod(ctx context.Context, name string) (*v1.Pod, error) { + if c.terminated { + return nil, ErrClientTerminated + } return c.clientset.CoreV1().Pods(c.namespace).Get(ctx, name, metav1.GetOptions{}) } diff --git a/pkg/k8s/pvc.go b/pkg/k8s/pvc.go index 7af2ad2d..5346c5de 100644 --- a/pkg/k8s/pvc.go +++ b/pkg/k8s/pvc.go @@ -16,6 +16,9 @@ func (c *Client) CreatePersistentVolumeClaim( labels map[string]string, size resource.Quantity, ) error { + if c.terminated { + return ErrClientTerminated + } if err := validatePVCName(name); err != nil { return err } diff --git a/pkg/k8s/replicaset.go b/pkg/k8s/replicaset.go index bf3adf90..32a83338 100644 --- a/pkg/k8s/replicaset.go +++ b/pkg/k8s/replicaset.go @@ -21,6 +21,9 @@ type ReplicaSetConfig struct { // CreateReplicaSet creates a new replicaSet in namespace that k8s is initialized with if it doesn't already exist. func (c *Client) CreateReplicaSet(ctx context.Context, rsConfig ReplicaSetConfig, init bool) (*appv1.ReplicaSet, error) { + if c.terminated { + return nil, ErrClientTerminated + } if err := validateReplicaSetConfig(rsConfig); err != nil { return nil, err } @@ -117,6 +120,9 @@ func (c *Client) GetFirstPodFromReplicaSet(ctx context.Context, name string) (*v } func (c *Client) getReplicaSet(ctx context.Context, name string) (*appv1.ReplicaSet, error) { + if c.terminated { + return nil, ErrClientTerminated + } return c.clientset.AppsV1().ReplicaSets(c.namespace).Get(ctx, name, metav1.GetOptions{}) } diff --git a/pkg/k8s/role.go b/pkg/k8s/role.go index a3c58dcd..ba5a8058 100644 --- a/pkg/k8s/role.go +++ b/pkg/k8s/role.go @@ -14,6 +14,9 @@ func (c *Client) CreateRole( labels map[string]string, policyRules []rbacv1.PolicyRule, ) error { + if c.terminated { + return ErrClientTerminated + } if err := validateRoleName(name); err != nil { return err } @@ -47,6 +50,9 @@ func (c *Client) CreateClusterRole( labels map[string]string, policyRules []rbacv1.PolicyRule, ) error { + if c.terminated { + return ErrClientTerminated + } if err := validateClusterRoleName(name); err != nil { return err } diff --git a/pkg/k8s/rolebinding.go b/pkg/k8s/rolebinding.go index 9d7a4987..760821fd 100644 --- a/pkg/k8s/rolebinding.go +++ b/pkg/k8s/rolebinding.go @@ -14,6 +14,9 @@ func (c *Client) CreateRoleBinding( labels map[string]string, role, serviceAccount string, ) error { + if c.terminated { + return ErrClientTerminated + } if err := validateRoleBindingName(name); err != nil { return err } @@ -60,6 +63,9 @@ func (c *Client) CreateClusterRoleBinding( labels map[string]string, clusterRole, serviceAccount string, ) error { + if c.terminated { + return ErrClientTerminated + } if err := validateClusterRoleBindingName(name); err != nil { return err } diff --git a/pkg/k8s/service.go b/pkg/k8s/service.go index 40dbc5e5..364b37f2 100644 --- a/pkg/k8s/service.go +++ b/pkg/k8s/service.go @@ -13,6 +13,9 @@ import ( ) func (c *Client) GetService(ctx context.Context, name string) (*v1.Service, error) { + if c.terminated { + return nil, ErrClientTerminated + } return c.clientset.CoreV1().Services(c.namespace).Get(ctx, name, metav1.GetOptions{}) } @@ -24,6 +27,9 @@ func (c *Client) CreateService( portsTCP, portsUDP []int, ) (*v1.Service, error) { + if c.terminated { + return nil, ErrClientTerminated + } if err := validateServiceName(name); err != nil { return nil, err } @@ -57,6 +63,9 @@ func (c *Client) PatchService( portsTCP, portsUDP []int, ) (*v1.Service, error) { + if c.terminated { + return nil, ErrClientTerminated + } if err := validateServiceName(name); err != nil { return nil, err } diff --git a/pkg/k8s/serviceaccount.go b/pkg/k8s/serviceaccount.go index b9c12147..00f5ff92 100644 --- a/pkg/k8s/serviceaccount.go +++ b/pkg/k8s/serviceaccount.go @@ -8,6 +8,9 @@ import ( ) func (c *Client) CreateServiceAccount(ctx context.Context, name string, labels map[string]string) error { + if c.terminated { + return ErrClientTerminated + } if err := validateServiceName(name); err != nil { return err } @@ -28,5 +31,8 @@ func (c *Client) CreateServiceAccount(ctx context.Context, name string, labels m } func (c *Client) DeleteServiceAccount(ctx context.Context, name string) error { + if c.terminated { + return ErrClientTerminated + } return c.clientset.CoreV1().ServiceAccounts(c.namespace).Delete(ctx, name, metav1.DeleteOptions{}) } diff --git a/pkg/k8s/types.go b/pkg/k8s/types.go index 6e1381bf..9e79e1b3 100644 --- a/pkg/k8s/types.go +++ b/pkg/k8s/types.go @@ -74,4 +74,5 @@ type KubeManager interface { UpdateDaemonSet(ctx context.Context, name string, labels map[string]string, initContainers []corev1.Container, containers []corev1.Container) (*appv1.DaemonSet, error) WaitForDeployment(ctx context.Context, name string) error WaitForService(ctx context.Context, name string) error + Terminate() } diff --git a/pkg/knuu/instance_old.go b/pkg/knuu/instance_old.go index 055f5b3d..29a58cd6 100644 --- a/pkg/knuu/instance_old.go +++ b/pkg/knuu/instance_old.go @@ -11,6 +11,7 @@ import ( "context" "errors" "io" + "time" v1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -58,7 +59,7 @@ func (i *Instance) SetImage(image string) error { if tmpKnuu == nil { return errors.New("tmpKnuu is not initialized") } - ctx, cancel := context.WithTimeout(context.Background(), tmpKnuu.timeout) + ctx, cancel := context.WithTimeout(context.Background(), time.Hour) defer cancel() return i.Instance.Build().SetImage(ctx, image) } diff --git a/pkg/knuu/knuu.go b/pkg/knuu/knuu.go index 06ce179d..7e4e5535 100644 --- a/pkg/knuu/knuu.go +++ b/pkg/knuu/knuu.go @@ -7,6 +7,7 @@ import ( "os" "os/signal" "strings" + "sync" "syscall" "time" @@ -29,12 +30,16 @@ const ( // FIXME: use supported kubernetes version images (use of latest could break) (https://github.com/celestiaorg/knuu/issues/116) timeoutHandlerImage = "docker.io/bitnami/kubectl:latest" + timeoutHandlerNameStop = timeoutHandlerName + "-stop" + timeoutHandlerTimeout = 1 * time.Second + ExitCodeSIGINT = 130 + TimeFormat = "20060102T150405Z" ) type Knuu struct { system.SystemDependencies - timeout time.Duration + stopMu sync.Mutex } type Options struct { @@ -61,13 +66,19 @@ func New(ctx context.Context, opts Options) (*Knuu, error) { Scope: opts.Scope, StartTime: time.Now().UTC().Format(TimeFormat), }, - timeout: opts.Timeout, } if err := setDefaults(ctx, k); err != nil { return nil, err } + if opts.Timeout == 0 { + opts.Timeout = defaultTimeout + } + if err := k.handleTimeout(ctx, opts.Timeout, timeoutHandlerName); err != nil { + return nil, ErrHandleTimeout.Wrap(err) + } + if opts.ProxyEnabled { if err := setupProxy(ctx, k); err != nil { return nil, err @@ -87,14 +98,20 @@ func (k *Knuu) HandleStopSignal(ctx context.Context) { go func() { <-stop k.Logger.Info("Received signal to stop, cleaning up resources...") - if err := k.CleanUp(ctx); err != nil { - k.Logger.Errorf("Error deleting namespace: %v", err) + // Lock the stop mutex to prevent multiple stop signals from being processed concurrently + k.stopMu.Lock() + defer k.stopMu.Unlock() + err := k.handleTimeout(ctx, timeoutHandlerTimeout, timeoutHandlerNameStop) + if err != nil { + k.Logger.Errorf("Error cleaning up resources with timeout handler: %v", err) } + k.K8sClient.Terminate() + os.Exit(ExitCodeSIGINT) }() } // handleTimeout creates a timeout handler that will delete all resources with the scope after the timeout -func (k *Knuu) handleTimeout(ctx context.Context) error { +func (k *Knuu) handleTimeout(ctx context.Context, timeout time.Duration, timeoutHandlerName string) error { inst, err := k.NewInstance(timeoutHandlerName) if err != nil { return ErrCannotCreateInstance.Wrap(err) @@ -112,7 +129,7 @@ func (k *Knuu) handleTimeout(ctx context.Context) error { // Wait for a specific period before executing the next operation. // This is useful to ensure that any previous operation has time to complete. - commands = append(commands, fmt.Sprintf("sleep %d", int64(k.timeout.Seconds()))) + commands = append(commands, fmt.Sprintf("sleep %d", int64(timeout.Seconds()))) // Collects all resources (pods, services, etc.) within the specified namespace that match a specific label, excluding certain types, // and then deletes them. This is useful for cleaning up specific test resources before proceeding to delete the namespace. commands = append(commands, @@ -184,10 +201,6 @@ func setDefaults(ctx context.Context, k *Knuu) error { } k.Scope = k8s.SanitizeName(k.Scope) - if k.timeout == 0 { - k.timeout = defaultTimeout - } - if k.K8sClient == nil { var err error k.K8sClient, err = k8s.NewClient(ctx, k.Scope, k.Logger) @@ -196,10 +209,6 @@ func setDefaults(ctx context.Context, k *Knuu) error { } } - if err := k.handleTimeout(ctx); err != nil { - return ErrHandleTimeout.Wrap(err) - } - if k.ImageBuilder == nil { k.ImageBuilder = &kaniko.Kaniko{ SystemDependencies: k.SystemDependencies, diff --git a/pkg/knuu/knuu_test.go b/pkg/knuu/knuu_test.go index fe99fca8..7617d371 100644 --- a/pkg/knuu/knuu_test.go +++ b/pkg/knuu/knuu_test.go @@ -74,7 +74,7 @@ func TestNew(t *testing.T) { assert.NotNil(t, k.K8sClient) assert.NotNil(t, k.ImageBuilder) assert.NotEmpty(t, k.Scope) - assert.Equal(t, defaultTimeout, k.timeout) + assert.Equal(t, defaultTimeout, defaultTimeout, timeoutHandlerName) }, }, { @@ -116,7 +116,7 @@ func TestNew(t *testing.T) { expectedError: nil, validateFunc: func(t *testing.T, k *Knuu) { assert.NotNil(t, k) - assert.Equal(t, 30*time.Minute, k.timeout) + assert.Equal(t, 30*time.Minute, defaultTimeout) }, }, { From 2e36eba9730b53a9b928c168eabc6f698e05dbd0 Mon Sep 17 00:00:00 2001 From: Mojtaba Date: Wed, 18 Sep 2024 18:35:44 +0330 Subject: [PATCH 2/2] fix: a broken test --- pkg/knuu/knuu_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pkg/knuu/knuu_test.go b/pkg/knuu/knuu_test.go index 7617d371..31d3ef10 100644 --- a/pkg/knuu/knuu_test.go +++ b/pkg/knuu/knuu_test.go @@ -107,18 +107,6 @@ func TestNew(t *testing.T) { assert.NotNil(t, k.Logger) }, }, - { - name: "With custom Timeout", - options: Options{ - Scope: "test", - Timeout: 30 * time.Minute, - }, - expectedError: nil, - validateFunc: func(t *testing.T, k *Knuu) { - assert.NotNil(t, k) - assert.Equal(t, 30*time.Minute, defaultTimeout) - }, - }, { name: "With custom Image Builder", options: Options{