diff --git a/go.mod b/go.mod index 7345580581a..ae78eb8263e 100755 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ replace ( cloud.google.com/go/secretmanager => cloud.google.com/go/secretmanager v1.11.2 cloud.google.com/go/storage => cloud.google.com/go/storage v1.33.0 cloud.google.com/go/trace => cloud.google.com/go/trace v1.10.2 - code.cloudfoundry.org/bytefmt => code.cloudfoundry.org/bytefmt v0.0.0-20230612151507-41ef4d1f67a4 + code.cloudfoundry.org/bytefmt => code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b contrib.go.opencensus.io/exporter/aws => contrib.go.opencensus.io/exporter/aws v0.0.0-20230502192102-15967c811cec contrib.go.opencensus.io/exporter/prometheus => contrib.go.opencensus.io/exporter/prometheus v0.4.2 contrib.go.opencensus.io/integrations/ocsql => contrib.go.opencensus.io/integrations/ocsql v0.1.7 @@ -24,7 +24,7 @@ replace ( github.com/Azure/azure-sdk-for-go => github.com/Azure/azure-sdk-for-go v68.0.0+incompatible github.com/Azure/azure-sdk-for-go/sdk/azcore => github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity => github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 - github.com/Azure/azure-sdk-for-go/sdk/internal => github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 + github.com/Azure/azure-sdk-for-go/sdk/internal => github.com/Azure/azure-sdk-for-go/sdk/internal v1.4.0 github.com/Azure/go-amqp => github.com/Azure/go-amqp v1.0.2 github.com/Azure/go-autorest => github.com/Azure/go-autorest v14.2.1-0.20230905222633-df94ce56f001+incompatible github.com/Azure/go-autorest/autorest => github.com/Azure/go-autorest/autorest v0.11.30-0.20230905222633-df94ce56f001 @@ -36,14 +36,14 @@ replace ( github.com/Azure/go-autorest/tracing => github.com/Azure/go-autorest/tracing v0.6.1-0.20230905222633-df94ce56f001 github.com/BurntSushi/toml => github.com/BurntSushi/toml v1.3.2 github.com/DATA-DOG/go-sqlmock => github.com/DATA-DOG/go-sqlmock v1.5.0 - github.com/GoogleCloudPlatform/cloudsql-proxy => github.com/GoogleCloudPlatform/cloudsql-proxy v1.33.11 + github.com/GoogleCloudPlatform/cloudsql-proxy => github.com/GoogleCloudPlatform/cloudsql-proxy v1.33.12 github.com/Masterminds/semver/v3 => github.com/Masterminds/semver/v3 v3.2.1 github.com/ajstarks/deck => github.com/ajstarks/deck v0.0.0-20231012031509-f833e437b68a github.com/ajstarks/deck/generate => github.com/ajstarks/deck/generate v0.0.0-20231012031509-f833e437b68a github.com/ajstarks/svgo => github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b github.com/antihax/optional => github.com/antihax/optional v1.0.0 github.com/armon/go-socks5 => github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 - github.com/aws/aws-sdk-go => github.com/aws/aws-sdk-go v1.45.26 + github.com/aws/aws-sdk-go => github.com/aws/aws-sdk-go v1.45.28 github.com/aws/aws-sdk-go-v2 => github.com/aws/aws-sdk-go-v2 v1.21.2 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream => github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.14 github.com/aws/aws-sdk-go-v2/config => github.com/aws/aws-sdk-go-v2/config v1.19.0 @@ -207,7 +207,7 @@ replace ( github.com/lucasb-eyer/go-colorful => github.com/lucasb-eyer/go-colorful v1.2.0 github.com/mailru/easyjson => github.com/mailru/easyjson v0.7.7 github.com/mattn/go-colorable => github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty => github.com/mattn/go-isatty v0.0.19 + github.com/mattn/go-isatty => github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.14.17 github.com/matttproud/golang_protobuf_extensions => github.com/matttproud/golang_protobuf_extensions v1.0.4 github.com/mitchellh/colorstring => github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db @@ -236,7 +236,7 @@ replace ( github.com/prashantv/gostub => github.com/prashantv/gostub v1.1.0 github.com/prometheus/client_golang => github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_model => github.com/prometheus/client_model v0.5.0 - github.com/prometheus/common => github.com/prometheus/common v0.44.0 + github.com/prometheus/common => github.com/prometheus/common v0.45.0 github.com/prometheus/procfs => github.com/prometheus/procfs v0.12.0 github.com/prometheus/prometheus => github.com/prometheus/prometheus v1.99.0 github.com/quasilyte/go-ruleguard => github.com/quasilyte/go-ruleguard v0.4.0 @@ -316,7 +316,7 @@ replace ( google.golang.org/genproto => google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b google.golang.org/genproto/googleapis/api => google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b google.golang.org/genproto/googleapis/rpc => google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b - google.golang.org/grpc => google.golang.org/grpc v1.58.3 + google.golang.org/grpc => google.golang.org/grpc v1.59.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc => google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 google.golang.org/protobuf => google.golang.org/protobuf v1.31.0 gopkg.in/check.v1 => gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c @@ -394,9 +394,8 @@ require ( golang.org/x/sync v0.4.0 golang.org/x/sys v0.13.0 golang.org/x/text v0.13.0 - golang.org/x/tools v0.14.0 golang.org/x/time v0.3.0 - golang.org/x/tools v0.13.0 + golang.org/x/tools v0.14.0 gonum.org/v1/hdf5 v0.0.0-00010101000000-000000000000 gonum.org/v1/plot v0.10.1 google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a @@ -469,7 +468,7 @@ require ( github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -478,7 +477,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.15.1 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect @@ -500,7 +499,6 @@ require ( golang.org/x/image v0.13.0 // indirect golang.org/x/mod v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect - golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect google.golang.org/api v0.139.0 // indirect diff --git a/go.sum b/go.sum index daa65b4519b..53ea96d6598 100644 --- a/go.sum +++ b/go.sum @@ -133,8 +133,8 @@ cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2Ms cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= -code.cloudfoundry.org/bytefmt v0.0.0-20230612151507-41ef4d1f67a4 h1:9G5F8zgma5v0GdDvNz6iZwwJp3RS/z0SY/aHGfVwvTo= -code.cloudfoundry.org/bytefmt v0.0.0-20230612151507-41ef4d1f67a4/go.mod h1:wYHCXH/gI19ujoFVuMkY48qPpPCoHLKBKXPkn67h/Yc= +code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b h1:/2OEIBwZAaJ8n8iTXrM4v/+bdyLDTLwcW6RZtkO4+r0= +code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b/go.mod h1:CKNYSQxmKcMCNIKoRG5rRR4AIgJMIoK65ya+Z5xHnk4= dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20221208032759-85de2813cf6b/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA= @@ -177,8 +177,8 @@ github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/P github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go v1.45.26 h1:PJ2NJNY5N/yeobLYe1Y+xLdavBi67ZI8gvph6ftwVCg= -github.com/aws/aws-sdk-go v1.45.26/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.45.28 h1:p2ATcaK6ffSw4yZ2UAGzgRyRXwKyOJY6ZCiKqj5miJE= +github.com/aws/aws-sdk-go v1.45.28/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.14 h1:Sc82v7tDQ/vdU1WtuSyzZ1I7y/68j//HJ6uozND1IDs= @@ -217,6 +217,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 h1:0BkLfgeDjfZnZ+MhB3ONb01u9pwF github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= @@ -464,11 +466,11 @@ github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLO github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mandolyte/mdtopdf v1.3.2/go.mod h1:c28Ldk+tVc/y7QQcEcILStS/OFlerdXGGdBUzJQBgEo= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= @@ -517,8 +519,8 @@ github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1 github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quasilyte/go-ruleguard v0.4.0 h1:DyM6r+TKL+xbKB4Nm7Afd1IQh9kEUKQs2pboWGKtvQo= @@ -623,8 +625,6 @@ golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= -go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8= golang.org/x/exp/shiny v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= @@ -673,8 +673,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go. google.golang.org/genproto/googleapis/bytestream v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:itlFWGBbEyD32PUeJsTG8h8Wz7iJXfVK4gt1EJ+pAG0= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/internal/k8s/job/job.go b/internal/k8s/job/job.go index 76fda87522c..5c00db89a0d 100644 --- a/internal/k8s/job/job.go +++ b/internal/k8s/job/job.go @@ -48,6 +48,9 @@ type reconciler struct { // Job is a type alias for the k8s job definition. type Job = batchv1.Job +// JobStatus is a type alias for the k8s job status definition. +type JobStatus = batchv1.JobStatus + // New returns the JobWatcher that implements reconciliation loop, or any errors occurred. func New(opts ...Option) (JobWatcher, error) { r := &reconciler{ diff --git a/internal/test/mock/controller_runtime.go b/internal/test/mock/controller_runtime.go new file mode 100644 index 00000000000..30a38354169 --- /dev/null +++ b/internal/test/mock/controller_runtime.go @@ -0,0 +1,67 @@ +// Copyright (C) 2019-2023 vdaas.org vald team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package mock + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type MockSubResourceWriter struct { + client.SubResourceWriter +} + +func (s *MockSubResourceWriter) Update(context.Context, client.Object, ...client.SubResourceUpdateOption) error { + return nil +} + +type MockClient struct { + client.Client +} + +func (*MockClient) Status() client.SubResourceWriter { + s := MockSubResourceWriter{ + SubResourceWriter: &MockSubResourceWriter{}, + } + return s.SubResourceWriter +} + +func (*MockClient) Get(context.Context, client.ObjectKey, client.Object, ...client.GetOption) error { + return nil +} + +func (*MockClient) Create(context.Context, client.Object, ...client.CreateOption) error { + return nil +} + +func (*MockClient) Delete(context.Context, client.Object, ...client.DeleteOption) error { + return nil +} + +func (*MockClient) DeleteAllOf(context.Context, client.Object, ...client.DeleteAllOfOption) error { + return nil +} + +type MockManager struct { + manager.Manager +} + +func (m *MockManager) GetClient() client.Client { + c := &MockClient{ + Client: &MockClient{}, + } + return c.Client +} diff --git a/pkg/tools/benchmark/job/service/insert.go b/pkg/tools/benchmark/job/service/insert.go index b38c3622b2b..d654ecc1a40 100644 --- a/pkg/tools/benchmark/job/service/insert.go +++ b/pkg/tools/benchmark/job/service/insert.go @@ -56,6 +56,10 @@ func (j *job) insert(ctx context.Context, ech chan error) error { case ech <- err: } } + // loopCnt represents the quotient of iter divided by the len(vecs). + // This is to account for when iter exceeds len(vecs). + // It is used to calculate idx to determine which index of vecs to access. + // idx takes between <0, len(vecs)-1>. loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) idx := iter - 1 - (len(vecs) * int(loopCnt)) res, err := j.client.Insert(egctx, &payload.Insert_Request{ @@ -71,6 +75,9 @@ func (j *job) insert(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } // TODO: send metrics to the Prometeus diff --git a/pkg/tools/benchmark/job/service/job.go b/pkg/tools/benchmark/job/service/job.go index b047051ea16..299b114172e 100644 --- a/pkg/tools/benchmark/job/service/job.go +++ b/pkg/tools/benchmark/job/service/job.go @@ -254,6 +254,7 @@ func (j *job) Start(ctx context.Context) (<-chan error, error) { cech, err := j.client.Start(ctx) if err != nil { log.Error("[benchmark job] failed to start connection monitor") + close(ech) return nil, err } j.eg.Go(func() error { @@ -280,6 +281,7 @@ func (j *job) Start(ctx context.Context) (<-chan error, error) { case ech <- err: } } + close(ech) if err := p.Signal(syscall.SIGTERM); err != nil { log.Error(err) } diff --git a/pkg/tools/benchmark/job/service/object.go b/pkg/tools/benchmark/job/service/object.go index 9284b50f95d..91732f78c27 100644 --- a/pkg/tools/benchmark/job/service/object.go +++ b/pkg/tools/benchmark/job/service/object.go @@ -56,6 +56,9 @@ func (j *job) exists(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return nil default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } log.Debugf("[benchmark job] Finish exists: iter= %d \n%v\n", idx, res) @@ -115,6 +118,9 @@ func (j *job) getObject(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return nil default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } if res != nil { diff --git a/pkg/tools/benchmark/job/service/remove.go b/pkg/tools/benchmark/job/service/remove.go index c8a6650bc2f..dbf58664aae 100644 --- a/pkg/tools/benchmark/job/service/remove.go +++ b/pkg/tools/benchmark/job/service/remove.go @@ -65,6 +65,9 @@ func (j *job) remove(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } log.Debugf("[benchmark job] Finish remove: iter= %d \n%v", idx, res) diff --git a/pkg/tools/benchmark/job/service/search.go b/pkg/tools/benchmark/job/service/search.go index 715b8f5356d..fe5f81f9f44 100644 --- a/pkg/tools/benchmark/job/service/search.go +++ b/pkg/tools/benchmark/job/service/search.go @@ -65,6 +65,10 @@ func (j *job) search(ctx context.Context, ech chan error) error { case ech <- err: } } + // loopCnt represents the quotient of iter divided by the len(vecs). + // This is to account for when iter exceeds len(vecs). + // It is used to calculate idx to determine which index of vecs to access. + // idx takes between <0, len(vecs)-1>. loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) idx := iter - 1 - (len(vecs) * int(loopCnt)) if len(vecs[idx]) != j.dimension { @@ -81,6 +85,9 @@ func (j *job) search(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return nil default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } if res != nil && j.searchConfig.EnableLinearSearch { @@ -116,6 +123,10 @@ func (j *job) search(ctx context.Context, ech chan error) error { } } log.Debugf("[benchmark job] Start linear search: iter = %d", iter) + // loopCnt represents the quotient of iter divided by the len(vecs). + // This is to account for when iter exceeds len(vecs). + // It is used to calculate idx to determine which index of vecs to access. + // idx takes between <0, len(vecs)-1>. loopCnt := math.Floor(float64(i-1) / float64(len(vecs))) idx := iter - 1 - (len(vecs) * int(loopCnt)) if len(vecs[idx]) != j.dimension { @@ -132,6 +143,9 @@ func (j *job) search(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } if res != nil { diff --git a/pkg/tools/benchmark/job/service/update.go b/pkg/tools/benchmark/job/service/update.go index f641f84f572..06a95ebaa3d 100644 --- a/pkg/tools/benchmark/job/service/update.go +++ b/pkg/tools/benchmark/job/service/update.go @@ -57,6 +57,10 @@ func (j *job) update(ctx context.Context, ech chan error) error { case ech <- err: } } + // loopCnt represents the quotient of iter divided by the len(vecs). + // This is to account for when iter exceeds len(vecs). + // It is used to calculate idx to determine which index of vecs to access. + // idx takes between <0, len(vecs)-1>. loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) idx := iter - 1 - (len(vecs) * int(loopCnt)) res, err := j.client.Update(egctx, &payload.Update_Request{ @@ -72,6 +76,9 @@ func (j *job) update(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } if res != nil { diff --git a/pkg/tools/benchmark/job/service/upsert.go b/pkg/tools/benchmark/job/service/upsert.go index 71a88cfda77..cf77479a79f 100644 --- a/pkg/tools/benchmark/job/service/upsert.go +++ b/pkg/tools/benchmark/job/service/upsert.go @@ -57,6 +57,10 @@ func (j *job) upsert(ctx context.Context, ech chan error) error { case ech <- err: } } + // loopCnt represents the quotient of iter divided by the len(vecs). + // This is to account for when iter exceeds len(vecs). + // It is used to calculate idx to determine which index of vecs to access. + // idx takes between <0, len(vecs)-1>. loopCnt := math.Floor(float64(iter-1) / float64(len(vecs))) idx := iter - 1 - (len(vecs) * int(loopCnt)) res, err := j.client.Upsert(egctx, &payload.Upsert_Request{ @@ -72,6 +76,9 @@ func (j *job) upsert(ctx context.Context, ech chan error) error { log.Errorf("[benchmark job] context error is detected: %s\t%s", err.Error(), egctx.Err()) return errors.Join(err, egctx.Err()) default: + // TODO: count up error for observe benchmark job + // We should wait for refactoring internal/o11y. + log.Errorf("[benchmark job] err: %s", err.Error()) } } if res != nil { diff --git a/pkg/tools/benchmark/operator/service/operator.go b/pkg/tools/benchmark/operator/service/operator.go index ef377b6227e..0f1139ab792 100644 --- a/pkg/tools/benchmark/operator/service/operator.go +++ b/pkg/tools/benchmark/operator/service/operator.go @@ -171,7 +171,7 @@ func (o *operator) jobReconcile(ctx context.Context, jobList map[string][]job.Jo if cjobs == nil { cjobs = map[string]string{} } - // jobStatus is used for update benchmark job resource status + // benchmarkJobStatus is used for update benchmark job resource status benchmarkJobStatus := make(map[string]v1.BenchmarkJobStatus) // jobNames is used for check whether cjobs has delted job. // If cjobs has the delted job, it will be remove the end of jobReconcile function. @@ -215,7 +215,7 @@ func (o *operator) jobReconcile(ctx context.Context, jobList map[string][]job.Jo log.Debug("[reconcile job] finish") } -// benchmarkJobReconcile gets the vald benchmark job resource list and create Job for running benchmark job. +// benchJobReconcile gets the vald benchmark job resource list and create Job for running benchmark job. func (o *operator) benchJobReconcile(ctx context.Context, benchJobList map[string]v1.ValdBenchmarkJob) { log.Debugf("[reconcile benchmark job resource] job list: %#v", benchJobList) if len(benchJobList) == 0 { @@ -228,6 +228,7 @@ func (o *operator) benchJobReconcile(ctx context.Context, benchJobList map[strin if cbjl == nil { cbjl = make(map[string]*v1.ValdBenchmarkJob, 0) } + // jobStatus is used for update benchmarkJob CR status if updating is needed. jobStatus := make(map[string]v1.BenchmarkJobStatus) for k := range benchJobList { // update scenario status @@ -475,7 +476,7 @@ func (o *operator) createJob(ctx context.Context, bjr v1.ValdBenchmarkJob) error benchjob.WithTTLSecondsAfterFinished(int32(bjr.Spec.TTLSecondsAfterFinished)), ) if err != nil { - return nil + return err } // create job c := o.ctrl.GetManager().GetClient() diff --git a/pkg/tools/benchmark/operator/service/operator_test.go b/pkg/tools/benchmark/operator/service/operator_test.go new file mode 100644 index 00000000000..61c2c76f46f --- /dev/null +++ b/pkg/tools/benchmark/operator/service/operator_test.go @@ -0,0 +1,4487 @@ +// Copyright (C) 2019-2023 vdaas.org vald team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package service + +import ( + "context" + "reflect" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/vdaas/vald/internal/config" + "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/k8s" + "github.com/vdaas/vald/internal/k8s/job" + v1 "github.com/vdaas/vald/internal/k8s/vald/benchmark/api/v1" + "github.com/vdaas/vald/internal/test/goleak" + "github.com/vdaas/vald/internal/test/mock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// mockCtrl is used for mock the request to the Kubernetes API. +type mockCtrl struct { + StartFunc func(ctx context.Context) (<-chan error, error) + GetManagerFunc func() k8s.Manager +} + +func (m *mockCtrl) Start(ctx context.Context) (<-chan error, error) { + return m.StartFunc(ctx) +} + +func (m *mockCtrl) GetManager() k8s.Manager { + return m.GetManagerFunc() +} + +func Test_operator_getAtomicScenario(t *testing.T) { + type fields struct { + scenarios atomic.Pointer[map[string]*scenario] + } + type want struct { + want map[string]*scenario + } + type test struct { + name string + fields fields + want want + checkFunc func(want, map[string]*scenario) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got map[string]*scenario) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + { + name: "get nil when atomic has no resource", + fields: fields{ + scenarios: atomic.Pointer[map[string]*scenario]{}, + }, + want: want{ + want: nil, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + { + name: "get scenarios when scenario list is stored", + fields: fields{ + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scneario-insert": v1.BenchmarkJobAvailable, + "scneario-search": v1.BenchmarkJobAvailable, + }, + }, + }) + return ap + }(), + }, + want: want{ + want: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scneario-insert": v1.BenchmarkJobAvailable, + "scneario-search": v1.BenchmarkJobAvailable, + }, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + scenarios: test.fields.scenarios, + } + + got := o.getAtomicScenario() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_getAtomicBenchJob(t *testing.T) { + type fields struct { + benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + } + type want struct { + want map[string]*v1.ValdBenchmarkJob + } + type test struct { + name string + fields fields + want want + checkFunc func(want, map[string]*v1.ValdBenchmarkJob) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got map[string]*v1.ValdBenchmarkJob) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + { + name: "get nil when atomic has no resource", + fields: fields{ + benchjobs: atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{}, + }, + want: want{ + want: nil, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + { + name: "get benchjobs when job list is stored", + fields: fields{ + benchjobs: func() atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + "scenario-search": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + } + ap.Store(&m) + return ap + }(), + }, + want: want{ + want: map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + "scenario-search": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + benchjobs: test.fields.benchjobs, + } + + got := o.getAtomicBenchJob() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_getAtomicJob(t *testing.T) { + type fields struct { + jobs atomic.Pointer[map[string]string] + } + type want struct { + want map[string]string + } + type test struct { + name string + fields fields + want want + checkFunc func(want, map[string]string) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, got map[string]string) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + { + name: "get nil when atomic has no resource", + fields: fields{ + jobs: atomic.Pointer[map[string]string]{}, + }, + want: want{ + want: nil, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + { + name: "get jobs when jobs has resource", + fields: fields{ + jobs: func() atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + m := map[string]string{ + "scenario-insert": "default", + "scenario-search": "default", + } + ap.Store(&m) + return ap + }(), + }, + want: want{ + want: map[string]string{ + "scenario-insert": "default", + "scenario-search": "default", + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + }, + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + jobs: test.fields.jobs, + } + + got := o.getAtomicJob() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_jobReconcile(t *testing.T) { + type args struct { + ctx context.Context + jobList map[string][]job.Job + } + type fields struct { + jobNamespace string + jobImage string + jobImagePullPolicy string + scenarios atomic.Pointer[map[string]*scenario] + benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs atomic.Pointer[map[string]string] + ctrl k8s.Controller + } + type want struct { + want map[string]string + } + type test struct { + name string + args args + fields fields + want want + checkFunc func(want, map[string]string) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, got map[string]string) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when the length of jobList is 0.", + args: args{ + ctx: ctx, + jobList: map[string][]job.Job{}, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + }, + want: want{ + want: map[string]string{}, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with new job whose namespace is same as jobNamespace and deleted job by etcd", + args: args{ + ctx: ctx, + jobList: map[string][]job.Job{ + "scenario-insert": { + { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + }, + Status: job.JobStatus{ + Active: 1, + }, + }, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + jobs: func() atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + m := map[string]string{ + "scenario-completed-insert": "default", + } + ap.Store(&m) + return ap + }(), + benchjobs: func() atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + } + ap.Store(&m) + return ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]string{ + "scenario-insert": "default", + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with completed job whose namespace is same as jobNamespace", + args: args{ + ctx: ctx, + jobList: map[string][]job.Job{ + "scenario-insert": { + { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-completed-insert", + Namespace: "default", + }, + Status: job.JobStatus{ + Active: 0, + Succeeded: 1, + CompletionTime: func() *metav1.Time { + t := &metav1.Time{ + Time: time.Now(), + } + return t + }(), + }, + }, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + jobs: func() atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + m := map[string]string{ + "scenario-completed-insert": "default", + } + ap.Store(&m) + return ap + }(), + benchjobs: func() atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + } + ap.Store(&m) + return ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]string{ + "scenario-completed-insert": "default", + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with job whose namespace is not same as jobNamespace", + args: args{ + ctx: ctx, + jobList: map[string][]job.Job{ + "scenario-insert": { + { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "benchmark", + }, + Status: job.JobStatus{ + Active: 1, + }, + }, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + ctrl: nil, + }, + want: want{ + want: map[string]string{}, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + jobNamespace: test.fields.jobNamespace, + jobImage: test.fields.jobImage, + jobImagePullPolicy: test.fields.jobImagePullPolicy, + benchjobs: test.fields.benchjobs, + jobs: test.fields.jobs, + ctrl: test.fields.ctrl, + } + + o.jobReconcile(test.args.ctx, test.args.jobList) + got := o.getAtomicJob() + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_benchJobReconcile(t *testing.T) { + type args struct { + ctx context.Context + benchJobList map[string]v1.ValdBenchmarkJob + } + type fields struct { + jobNamespace string + jobImage string + jobImagePullPolicy string + scenarios atomic.Pointer[map[string]*scenario] + benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs atomic.Pointer[map[string]string] + ctrl k8s.Controller + } + type want struct { + scenarios map[string]*scenario + benchjobs map[string]*v1.ValdBenchmarkJob + } + type test struct { + name string + args args + fields fields + want want + checkFunc func(want, map[string]*scenario, map[string]*v1.ValdBenchmarkJob) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, gotS map[string]*scenario, gotJ map[string]*v1.ValdBenchmarkJob) error { + if !reflect.DeepEqual(w.scenarios, gotS) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotS, w.scenarios) + } + if !reflect.DeepEqual(w.benchjobs, gotJ) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotJ, w.benchjobs) + } + return nil + } + tests := []test{ + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when benchJobList is empty", + args: args{ + ctx: ctx, + benchJobList: map[string]v1.ValdBenchmarkJob{}, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + ctrl: nil, + }, + want: want{ + benchjobs: map[string]*v1.ValdBenchmarkJob{}, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when benchJobList has new benchmark Job with owner reference (reconcile after submitted scenario)", + args: args{ + ctx: ctx, + benchJobList: map[string]v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + }, + } + ap.Store(&m) + return ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + scenarios: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": "", + }, + }, + }, + benchjobs: map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when benchJobList has updated benchmark Job with owner reference (reconcile after updated scenario)", + args: args{ + ctx: ctx, + benchJobList: map[string]v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 2, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 2, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + }, + }, + } + ap.Store(&m) + return ap + }(), + benchjobs: func() atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + } + ap.Store(&m) + return ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + scenarios: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 2, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + }, + }, + }, + benchjobs: map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 2, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when benchJobList has updated benchmark Job with owner reference (reconcile after updated job status)", + args: args{ + ctx: ctx, + benchJobList: map[string]v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": "", + }, + }, + } + ap.Store(&m) + return ap + }(), + benchjobs: func() atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: "", + }, + } + ap.Store(&m) + return ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + scenarios: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + }, + }, + }, + benchjobs: map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success when benchJobList has new benchmark Job with owner reference and benchJob has deleted job", + args: args{ + ctx: ctx, + benchJobList: map[string]v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": "", + }, + }, + } + ap.Store(&m) + return ap + }(), + benchjobs: func() atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + m := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: "", + }, + "scenario-deleted-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-deleted-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario-deleted", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobCompleted, + }, + } + ap.Store(&m) + return ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + scenarios: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + }, + }, + }, + benchjobs: map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Name: "scenario", + }, + }, + Generation: 1, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + jobNamespace: test.fields.jobNamespace, + jobImage: test.fields.jobImage, + jobImagePullPolicy: test.fields.jobImagePullPolicy, + scenarios: test.fields.scenarios, + benchjobs: test.fields.benchjobs, + jobs: test.fields.jobs, + ctrl: test.fields.ctrl, + } + + o.benchJobReconcile(test.args.ctx, test.args.benchJobList) + gotS := o.getAtomicScenario() + gotJ := o.getAtomicBenchJob() + if err := checkFunc(test.want, gotS, gotJ); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_benchScenarioReconcile(t *testing.T) { + type args struct { + ctx context.Context + scenarioList map[string]v1.ValdBenchmarkScenario + } + type fields struct { + jobNamespace string + jobImage string + jobImagePullPolicy string + scenarios atomic.Pointer[map[string]*scenario] + benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs atomic.Pointer[map[string]string] + ctrl k8s.Controller + } + type want struct { + want map[string]*scenario + } + type test struct { + name string + args args + fields fields + want want + checkFunc func(want, map[string]*scenario) error + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) + } + defaultCheckFunc := func(w want, got map[string]*scenario) error { + if len(w.want) != len(got) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + for k, ws := range w.want { + gs, ok := got[k] + if !ok { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + // check CRD + if !reflect.DeepEqual(ws.Crd, gs.Crd) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + // check benchJobStatus + if len(ws.BenchJobStatus) != len(gs.BenchJobStatus) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + for k, v := range gs.BenchJobStatus { + sk := strings.Split(k, "-") + wk := strings.Join(sk[:len(sk)-1], "-") + if v != ws.BenchJobStatus[wk] { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + } + } + return nil + } + tests := []test{ + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with scenarioList is empty", + args: args{ + ctx: ctx, + scenarioList: map[string]v1.ValdBenchmarkScenario{}, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + ctrl: nil, + }, + want: want{ + want: map[string]*scenario{}, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with scenarioList has new scenario with no scenario has been applied yet.", + args: args{ + ctx: ctx, + scenarioList: map[string]v1.ValdBenchmarkScenario{ + "scenario": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobNotReady, + "scenario-search": v1.BenchmarkJobNotReady, + }, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with scenarioList has only status updated scenario.", + args: args{ + ctx: ctx, + scenarioList: map[string]v1.ValdBenchmarkScenario{ + "scenario": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert-1234567890": v1.BenchmarkJobNotReady, + "scenario-search-1234567891": v1.BenchmarkJobNotReady, + }, + }, + } + ap.Store(&m) + return ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobNotReady, + "scenario-search": v1.BenchmarkJobNotReady, + }, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with scenarioList has updated scenario when job is already running", + args: args{ + ctx: ctx, + scenarioList: map[string]v1.ValdBenchmarkScenario{ + "scenario": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 2, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + "scenario-search": v1.BenchmarkJobAvailable, + }, + }, + } + ap.Store(&m) + return ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 2, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 20000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 20000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobNotReady, + "scenario-search": v1.BenchmarkJobNotReady, + }, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + func() test { + ctx, cancel := context.WithCancel(context.Background()) + return test{ + name: "success with scenarioList has another scenario when scenario is already running", + args: args{ + ctx: ctx, + scenarioList: map[string]v1.ValdBenchmarkScenario{ + "scenario-v2": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-v2", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + }, + }, + fields: fields{ + jobNamespace: "default", + jobImage: "vdaas/vald-benchmark-job", + jobImagePullPolicy: "Always", + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + m := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioAvailable, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobAvailable, + "scenario-search": v1.BenchmarkJobAvailable, + }, + }, + } + ap.Store(&m) + return ap + }(), + ctrl: &mockCtrl{ + StartFunc: func(ctx context.Context) (<-chan error, error) { + return nil, nil + }, + GetManagerFunc: func() k8s.Manager { + m := &mock.MockManager{ + Manager: &mock.MockManager{}, + } + return m + }, + }, + }, + want: want{ + want: map[string]*scenario{ + "scenario-v2": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-v2", + Namespace: "default", + Generation: 1, + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 5, + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-v2-insert": v1.BenchmarkJobNotReady, + "scenario-v2-search": v1.BenchmarkJobNotReady, + }, + }, + }, + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + cancel() + }, + } + }(), + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(tt, test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + jobNamespace: test.fields.jobNamespace, + jobImage: test.fields.jobImage, + jobImagePullPolicy: test.fields.jobImagePullPolicy, + scenarios: test.fields.scenarios, + benchjobs: test.fields.benchjobs, + jobs: test.fields.jobs, + ctrl: test.fields.ctrl, + } + + o.benchScenarioReconcile(test.args.ctx, test.args.scenarioList) + got := o.getAtomicScenario() + t.Log(got["scenario"]) + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +func Test_operator_checkAtomics(t *testing.T) { + type fields struct { + scenarios atomic.Pointer[map[string]*scenario] + benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] + jobs atomic.Pointer[map[string]string] + } + type want struct { + err error + } + type test struct { + name string + fields fields + want want + checkFunc func(want, error) error + beforeFunc func(*testing.T) + afterFunc func(*testing.T) + } + defaultCheckFunc := func(w want, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + return nil + } + defaultScenarioMap := map[string]*scenario{ + "scenario": { + Crd: &v1.ValdBenchmarkScenario{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario", + Namespace: "default", + }, + Spec: v1.ValdBenchmarkScenarioSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + Jobs: []*v1.BenchmarkJobSpec{ + { + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + { + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + { + JobType: "update", + UpdateConfig: &config.UpdateConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + }, + }, + Status: v1.BenchmarkScenarioHealthy, + }, + BenchJobStatus: map[string]v1.BenchmarkJobStatus{ + "scenario-insert": v1.BenchmarkJobCompleted, + "scenario-search": v1.BenchmarkJobAvailable, + "scenario-update": v1.BenchmarkJobAvailable, + }, + }, + } + defaultBenchJobMap := map[string]*v1.ValdBenchmarkJob{ + "scenario-insert": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-insert", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Kind: ScenarioKind, + Name: "scenario", + }, + }, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "insert", + InsertConfig: &config.InsertConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobCompleted, + }, + "scenario-search": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-search", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Kind: ScenarioKind, + Name: "scenario", + }, + }, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "search", + SearchConfig: &config.SearchConfig{ + Epsilon: 0.1, + Radius: -1, + Num: 10, + MinNum: 10, + Timeout: "10s", + EnableLinearSearch: false, + AggregationAlgorithm: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + "scenario-update": { + ObjectMeta: metav1.ObjectMeta{ + Name: "scenario-update", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + Kind: ScenarioKind, + Name: "scenario", + }, + }, + }, + Spec: v1.BenchmarkJobSpec{ + Target: &v1.BenchmarkTarget{ + Host: "localhost", + Port: 8080, + }, + Dataset: &v1.BenchmarkDataset{ + Name: "fashion-minsit", + Group: "train", + Indexes: 10000, + Range: &config.BenchmarkDatasetRange{ + Start: 0, + End: 10000, + }, + URL: "", + }, + JobType: "update", + UpdateConfig: &config.UpdateConfig{ + SkipStrictExistCheck: false, + Timestamp: "", + }, + }, + Status: v1.BenchmarkJobAvailable, + }, + } + defaultJobMap := map[string]string{ + "scenario-insert": "default", + "scenario-search": "default", + "scenario-update": "default", + } + tests := []test{ + func() test { + return test{ + name: "return nil with no mismatch atmoics", + fields: fields{ + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&defaultScenarioMap) + return ap + }(), + benchjobs: func() atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + ap.Store(&defaultBenchJobMap) + return ap + }(), + jobs: func() atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + ap.Store(&defaultJobMap) + return ap + }(), + }, + want: want{}, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + } + }(), + func() test { + return test{ + name: "return mismatch error when scneario and job has atomic and benchJob has no atomic", + fields: fields{ + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&defaultScenarioMap) + return ap + }(), + jobs: func() atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + ap.Store(&defaultJobMap) + return ap + }(), + }, + want: want{ + err: errors.ErrMismatchBenchmarkAtomics(defaultJobMap, map[string]*v1.ValdBenchmarkJob{}, defaultScenarioMap), + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + } + }(), + func() test { + benchJobMap := map[string]*v1.ValdBenchmarkJob{} + for k, v := range defaultBenchJobMap { + val := v1.ValdBenchmarkJob{} + val = *v + benchJobMap[k] = &val + } + benchJobMap["scenario-search"].SetNamespace("benchmark") + return test{ + name: "return mismatch error when benchJob with different namespace", + fields: fields{ + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&defaultScenarioMap) + return ap + }(), + benchjobs: func() atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + ap.Store(&benchJobMap) + return ap + }(), + jobs: func() atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + ap.Store(&defaultJobMap) + return ap + }(), + }, + want: want{ + err: errors.ErrMismatchBenchmarkAtomics(defaultJobMap, benchJobMap, defaultScenarioMap), + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + } + }(), + func() test { + benchJobMap := map[string]*v1.ValdBenchmarkJob{} + for k, v := range defaultBenchJobMap { + val := v1.ValdBenchmarkJob{} + val = *v + benchJobMap[k] = &val + } + benchJobMap["scenario-search"].Status = v1.BenchmarkJobNotReady + return test{ + name: "return mismatch error when status is not same between benchJob and scenario.BenchJobStatus", + fields: fields{ + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&defaultScenarioMap) + return ap + }(), + benchjobs: func() atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + ap.Store(&benchJobMap) + return ap + }(), + jobs: func() atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + ap.Store(&defaultJobMap) + return ap + }(), + }, + want: want{ + err: errors.ErrMismatchBenchmarkAtomics(defaultJobMap, benchJobMap, defaultScenarioMap), + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + } + }(), + func() test { + benchJobMap := map[string]*v1.ValdBenchmarkJob{} + for k, v := range defaultBenchJobMap { + val := v1.ValdBenchmarkJob{} + val = *v + benchJobMap[k] = &val + } + ors := []metav1.OwnerReference{} + for _, v := range benchJobMap["scenario-search"].OwnerReferences { + or := v.DeepCopy() + or.Name = "incorrectName" + ors = append(ors, *or) + } + benchJobMap["scenario-search"].OwnerReferences = ors + return test{ + name: "return mismatch error when scenario does not have key of bench job owners scenario", + fields: fields{ + scenarios: func() atomic.Pointer[map[string]*scenario] { + ap := atomic.Pointer[map[string]*scenario]{} + ap.Store(&defaultScenarioMap) + return ap + }(), + benchjobs: func() atomic.Pointer[map[string]*v1.ValdBenchmarkJob] { + ap := atomic.Pointer[map[string]*v1.ValdBenchmarkJob]{} + ap.Store(&benchJobMap) + return ap + }(), + jobs: func() atomic.Pointer[map[string]string] { + ap := atomic.Pointer[map[string]string]{} + ap.Store(&defaultJobMap) + return ap + }(), + }, + want: want{ + err: errors.ErrMismatchBenchmarkAtomics(defaultJobMap, benchJobMap, defaultScenarioMap), + }, + checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T) { + t.Helper() + }, + afterFunc: func(t *testing.T) { + t.Helper() + }, + } + }(), + } + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(tt) + } + if test.afterFunc != nil { + defer test.afterFunc(tt) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + o := &operator{ + scenarios: test.fields.scenarios, + benchjobs: test.fields.benchjobs, + jobs: test.fields.jobs, + } + + err := o.checkAtomics() + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + }) + } +} + +// NOT IMPLEMENTED BELOW +// func TestNew(t *testing.T) { +// type args struct { +// opts []Option +// } +// type want struct { +// want Operator +// err error +// } +// type test struct { +// name string +// args args +// want want +// checkFunc func(want, Operator, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, got Operator, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// if !reflect.DeepEqual(got, w.want) { +// return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// // { +// // name: "test_case_1", +// // args: args{ +// // opts: nil, +// // }, +// // want: want{ +// // want: func() Operator { +// // o := &operator{ +// // jobNamespace: "default", +// // jobImage: "vdaas/vald-benchmark-job", +// // jobImagePullPolicy: "Always", +// // rcd: 10 * time.Second, +// // } +// // return o +// // }(), +// // }, +// // checkFunc: defaultCheckFunc, +// // beforeFunc: func(t *testing.T, args args) { +// // t.Helper() +// // }, +// // afterFunc: func(t *testing.T, args args) { +// // t.Helper() +// // }, +// // }, +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// opts:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// +// got, err := New(test.args.opts...) +// if err := checkFunc(test.want, got, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } + +// +// func Test_operator_PreStart(t *testing.T) { +// type args struct { +// ctx context.Context +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.PreStart(test.args.ctx) +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// +// }) +// } +// } +// +// func Test_operator_Start(t *testing.T) { +// type args struct { +// ctx context.Context +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// want <-chan error +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, <-chan error, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, got <-chan error, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// if !reflect.DeepEqual(got, w.want) { +// return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// got, err := o.Start(test.args.ctx) +// if err := checkFunc(test.want, got, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// +// }) +// } +// } + +// func Test_operator_initCtrl(t *testing.T) { +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T) +// afterFunc func(*testing.T) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// +// tests := []test{ +// // { +// // name: "test_case_1", +// // fields: fields{ +// // jobNamespace: "default", +// // jobImage: "vdaas/vald-benchmark-job", +// // jobImagePullPolicy: "Always", +// // // scenarios:nil, +// // // benchjobs:nil, +// // // jobs:nil, +// // // rcd:nil, +// // eg: nil, +// // ctrl: nil, +// // }, +// // want: want{ +// // err: errors.New("hoge"), +// // }, +// // checkFunc: defaultCheckFunc, +// // beforeFunc: func(t *testing.T) { +// // t.Helper() +// // }, +// // afterFunc: func(t *testing.T) { +// // t.Helper() +// // }, +// // }, +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T,) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T,) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.initCtrl() +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } + +// func Test_operator_deleteBenchmarkJob(t *testing.T) { +// type args struct { +// ctx context.Context +// name string +// generation int64 +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// name:"", +// generation:0, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// name:"", +// generation:0, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.deleteBenchmarkJob(test.args.ctx, test.args.name, test.args.generation) +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_deleteJob(t *testing.T) { +// type args struct { +// ctx context.Context +// name string +// generation int64 +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// name:"", +// generation:0, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// name:"", +// generation:0, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.deleteJob(test.args.ctx, test.args.name, test.args.generation) +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_createBenchmarkJob(t *testing.T) { +// type args struct { +// ctx context.Context +// scenario v1.ValdBenchmarkScenario +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// want []string +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, []string, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, got []string, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// if !reflect.DeepEqual(got, w.want) { +// return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// scenario:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// scenario:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// got, err := o.createBenchmarkJob(test.args.ctx, test.args.scenario) +// if err := checkFunc(test.want, got, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_createJob(t *testing.T) { +// type args struct { +// ctx context.Context +// bjr v1.ValdBenchmarkJob +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// bjr:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// bjr:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.createJob(test.args.ctx, test.args.bjr) +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_updateBenchmarkScenarioStatus(t *testing.T) { +// type args struct { +// ctx context.Context +// ss map[string]v1.ValdBenchmarkScenarioStatus +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// want []string +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, []string, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, got []string, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// if !reflect.DeepEqual(got, w.want) { +// return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// ss:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// ss:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// got, err := o.updateBenchmarkScenarioStatus(test.args.ctx, test.args.ss) +// if err := checkFunc(test.want, got, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_updateBenchmarkJobStatus(t *testing.T) { +// type args struct { +// ctx context.Context +// js map[string]v1.BenchmarkJobStatus +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// want []string +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, []string, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, got []string, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// if !reflect.DeepEqual(got, w.want) { +// return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) +// } +// return nil +// } +// tests := []test{ +// // TODO test cases +// /* +// { +// name: "test_case_1", +// args: args { +// ctx:nil, +// js:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// }, +// */ +// +// // TODO test cases +// /* +// func() test { +// return test { +// name: "test_case_2", +// args: args { +// ctx:nil, +// js:nil, +// }, +// fields: fields { +// jobNamespace:"", +// jobImage:"", +// jobImagePullPolicy:"", +// scenarios:nil, +// benchjobs:nil, +// jobs:nil, +// rcd:nil, +// eg:nil, +// ctrl:nil, +// }, +// want: want{}, +// checkFunc: defaultCheckFunc, +// beforeFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// afterFunc: func(t *testing.T, args args) { +// t.Helper() +// }, +// } +// }(), +// */ +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// got, err := o.updateBenchmarkJobStatus(test.args.ctx, test.args.js) +// if err := checkFunc(test.want, got, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// } +// +// func Test_operator_checkJobsStatus(t *testing.T) { +// type args struct { +// ctx context.Context +// jobs map[string]string +// } +// type fields struct { +// jobNamespace string +// jobImage string +// jobImagePullPolicy string +// scenarios atomic.Pointer[map[string]*scenario] +// benchjobs atomic.Pointer[map[string]*v1.ValdBenchmarkJob] +// jobs atomic.Pointer[map[string]string] +// rcd time.Duration +// eg errgroup.Group +// ctrl k8s.Controller +// } +// type want struct { +// err error +// } +// type test struct { +// name string +// args args +// fields fields +// want want +// checkFunc func(want, error) error +// beforeFunc func(*testing.T, args) +// afterFunc func(*testing.T, args) +// } +// defaultCheckFunc := func(w want, err error) error { +// if !errors.Is(err, w.err) { +// return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) +// } +// return nil +// } +// tests := []test{ +// // func() test { +// // return test{ +// // name: "test_case_2", +// // args: args{ +// // ctx: nil, +// // jobs: nil, +// // }, +// // fields: fields{ +// // jobNamespace: "", +// // jobImage: "", +// // jobImagePullPolicy: "", +// // scenarios: nil, +// // benchjobs: nil, +// // jobs: nil, +// // rcd: nil, +// // eg: nil, +// // ctrl: nil, +// // }, +// // want: want{}, +// // checkFunc: defaultCheckFunc, +// // beforeFunc: func(t *testing.T, args args) { +// // t.Helper() +// // }, +// // afterFunc: func(t *testing.T, args args) { +// // t.Helper() +// // }, +// // } +// // }(), +// } +// +// for _, tc := range tests { +// test := tc +// t.Run(test.name, func(tt *testing.T) { +// tt.Parallel() +// defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) +// if test.beforeFunc != nil { +// test.beforeFunc(tt, test.args) +// } +// if test.afterFunc != nil { +// defer test.afterFunc(tt, test.args) +// } +// checkFunc := test.checkFunc +// if test.checkFunc == nil { +// checkFunc = defaultCheckFunc +// } +// o := &operator{ +// jobNamespace: test.fields.jobNamespace, +// jobImage: test.fields.jobImage, +// jobImagePullPolicy: test.fields.jobImagePullPolicy, +// scenarios: test.fields.scenarios, +// benchjobs: test.fields.benchjobs, +// jobs: test.fields.jobs, +// rcd: test.fields.rcd, +// eg: test.fields.eg, +// ctrl: test.fields.ctrl, +// } +// +// err := o.checkJobsStatus(test.args.ctx, test.args.jobs) +// if err := checkFunc(test.want, err); err != nil { +// tt.Errorf("error = %v", err) +// } +// }) +// } +// }