2023-08-07 19:13:26 +00:00
// Copyright 2022 Woodpecker 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.
2022-09-05 04:01:14 +00:00
package kubernetes
import (
2023-12-19 03:53:52 +00:00
"context"
2023-12-19 01:38:18 +00:00
"fmt"
2024-01-12 22:57:24 +00:00
"strings"
2023-12-19 01:38:18 +00:00
2023-12-19 03:53:52 +00:00
"github.com/rs/zerolog/log"
2022-09-05 04:01:14 +00:00
v1 "k8s.io/api/core/v1"
2023-12-19 03:53:52 +00:00
"k8s.io/apimachinery/pkg/api/errors"
2022-09-05 04:01:14 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
2024-01-11 18:17:07 +00:00
"go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types"
2022-09-05 04:01:14 +00:00
)
2024-01-11 15:32:37 +00:00
const (
2024-02-17 11:30:06 +00:00
ServiceLabel = "service"
servicePrefix = "wp-svc-"
2024-01-11 15:32:37 +00:00
)
2024-01-15 02:59:08 +00:00
func mkService ( step * types . Step , config * config ) ( * v1 . Service , error ) {
2024-01-11 15:32:37 +00:00
name , err := serviceName ( step )
if err != nil {
return nil , err
}
selector := map [ string ] string {
ServiceLabel : name ,
}
2023-12-19 03:53:52 +00:00
2022-09-05 04:01:14 +00:00
var svcPorts [ ] v1 . ServicePort
2024-01-11 15:32:37 +00:00
for _ , port := range step . Ports {
2024-01-12 22:57:24 +00:00
svcPorts = append ( svcPorts , servicePort ( port ) )
2022-09-05 04:01:14 +00:00
}
2024-01-12 22:57:24 +00:00
log . Trace ( ) . Str ( "name" , name ) . Interface ( "selector" , selector ) . Interface ( "ports" , svcPorts ) . Msg ( "creating service" )
2022-09-05 04:01:14 +00:00
return & v1 . Service {
ObjectMeta : metav1 . ObjectMeta {
2023-12-19 03:53:52 +00:00
Name : name ,
2024-01-15 02:59:08 +00:00
Namespace : config . Namespace ,
2022-09-05 04:01:14 +00:00
} ,
Spec : v1 . ServiceSpec {
2023-12-19 03:53:52 +00:00
Type : v1 . ServiceTypeClusterIP ,
Selector : selector ,
Ports : svcPorts ,
2022-09-05 04:01:14 +00:00
} ,
2024-01-11 15:32:37 +00:00
} , nil
2022-09-05 04:01:14 +00:00
}
2023-12-19 03:53:52 +00:00
func serviceName ( step * types . Step ) ( string , error ) {
2024-02-17 11:30:06 +00:00
return dnsName ( servicePrefix + step . UUID + "-" + step . Name )
2023-12-19 03:53:52 +00:00
}
2024-01-12 22:57:24 +00:00
func servicePort ( port types . Port ) v1 . ServicePort {
portNumber := int32 ( port . Number )
portProtocol := strings . ToUpper ( port . Protocol )
return v1 . ServicePort {
Name : fmt . Sprintf ( "port-%d" , portNumber ) ,
Port : portNumber ,
Protocol : v1 . Protocol ( portProtocol ) ,
TargetPort : intstr . IntOrString { IntVal : portNumber } ,
}
}
2023-12-19 03:53:52 +00:00
func startService ( ctx context . Context , engine * kube , step * types . Step ) ( * v1 . Service , error ) {
2024-01-15 02:59:08 +00:00
engineConfig := engine . getConfig ( )
svc , err := mkService ( step , engineConfig )
2023-12-19 03:53:52 +00:00
if err != nil {
return nil , err
}
2024-01-11 15:32:37 +00:00
log . Trace ( ) . Str ( "name" , svc . Name ) . Interface ( "selector" , svc . Spec . Selector ) . Interface ( "ports" , svc . Spec . Ports ) . Msg ( "creating service" )
2024-01-15 02:59:08 +00:00
return engine . client . CoreV1 ( ) . Services ( engineConfig . Namespace ) . Create ( ctx , svc , metav1 . CreateOptions { } )
2023-12-19 03:53:52 +00:00
}
func stopService ( ctx context . Context , engine * kube , step * types . Step , deleteOpts metav1 . DeleteOptions ) error {
svcName , err := serviceName ( step )
if err != nil {
return err
}
2024-01-11 18:17:07 +00:00
log . Trace ( ) . Str ( "name" , svcName ) . Msg ( "deleting service" )
2023-12-19 03:53:52 +00:00
err = engine . client . CoreV1 ( ) . Services ( engine . config . Namespace ) . Delete ( ctx , svcName , deleteOpts )
if errors . IsNotFound ( err ) {
// Don't abort on 404 errors from k8s, they most likely mean that the pod hasn't been created yet, usually because pipeline was canceled before running all steps.
2024-01-11 18:17:07 +00:00
log . Trace ( ) . Err ( err ) . Msgf ( "unable to delete service %s" , svcName )
2023-12-19 03:53:52 +00:00
return nil
}
return err
}