11package util
22
33import (
4+ "bytes"
45 "context"
6+ "fmt"
7+ "strings"
58 "time"
69
710 g "github.com/onsi/ginkgo/v2"
@@ -10,13 +13,18 @@ import (
1013 corev1 "k8s.io/api/core/v1"
1114 apierrors "k8s.io/apimachinery/pkg/api/errors"
1215 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16+ "k8s.io/apimachinery/pkg/labels"
1317 "k8s.io/apimachinery/pkg/util/wait"
1418 "k8s.io/client-go/kubernetes"
19+ "k8s.io/client-go/kubernetes/scheme"
1520 "k8s.io/client-go/rest"
1621 "k8s.io/client-go/tools/clientcmd"
22+ "k8s.io/client-go/tools/remotecommand"
1723
1824 configv1 "github.com/openshift/api/config/v1"
1925 clientconfigv1 "github.com/openshift/client-go/config/clientset/versioned"
26+
27+ "github.com/openshift/cluster-version-operator/pkg/external"
2028)
2129
2230// IsHypershift checks if running on a HyperShift hosted cluster
@@ -135,3 +143,72 @@ const (
135143 // fauxinnati mocks Cincinnati Update Graph Server for OpenShift
136144 FauxinnatiAPIURL = "https://fauxinnati-fauxinnati.apps.ota-stage.q2z4.p1.openshiftapps.com/api/upgrades_info/graph"
137145)
146+
147+ func accessible (ctx context.Context , restConfig * rest.Config , urls ... string ) (bool , error ) {
148+ ctx , cancel := context .WithTimeout (ctx , 30 * time .Second )
149+ defer cancel ()
150+ kubeClient , err := GetKubeClient (restConfig )
151+ if err != nil {
152+ return false , err
153+ }
154+
155+ pods , err := kubeClient .CoreV1 ().Pods (external .DefaultCVONamespace ).
156+ List (ctx , metav1.ListOptions {LabelSelector : labels .FormatLabels (map [string ]string {"k8s-app" : "cluster-version-operator" })})
157+ if err != nil || len (pods .Items ) == 0 {
158+ return false , fmt .Errorf ("could not find CVO pod: %w" , err )
159+ }
160+ podName := pods .Items [0 ].Name
161+ command := []string {"curl" , "-sI" , "--max-time" , "10" }
162+ command = append (command , urls ... )
163+
164+ req := kubeClient .CoreV1 ().RESTClient ().Post ().
165+ Resource ("pods" ).
166+ Name (podName ).
167+ Namespace (external .DefaultCVONamespace ).
168+ SubResource ("exec" ).
169+ VersionedParams (& corev1.PodExecOptions {
170+ Command : command ,
171+ Stdout : true ,
172+ Stderr : true ,
173+ }, scheme .ParameterCodec )
174+ exec , err := remotecommand .NewSPDYExecutor (restConfig , "POST" , req .URL ())
175+
176+ if err != nil {
177+ return false , err
178+ }
179+ stdoutBuf := & bytes.Buffer {}
180+ stderrBuf := & bytes.Buffer {}
181+
182+ err = exec .StreamWithContext (ctx , remotecommand.StreamOptions {
183+ Stdout : stdoutBuf ,
184+ Stderr : stderrBuf ,
185+ })
186+ if err != nil {
187+ if strings .Contains (err .Error (), "command terminated with exit code" ) {
188+ return false , nil
189+ }
190+ return false , err
191+ }
192+ return true , nil
193+ }
194+
195+ // NetworkRestricted returns true if there is a given URL
196+ // that is not accessible. Otherwise, false.
197+ func NetworkRestricted (ctx context.Context , restConfig * rest.Config , urls ... string ) (bool , error ) {
198+ ok , err := accessible (ctx , restConfig , urls ... )
199+ if err != nil {
200+ return false , err
201+ }
202+ return ! ok , nil
203+ }
204+
205+ func SkipIfNetworkRestricted (ctx context.Context , restConfig * rest.Config , urls ... string ) error {
206+ ok , err := NetworkRestricted (ctx , restConfig , urls ... )
207+ if err != nil {
208+ return err
209+ }
210+ if ok {
211+ g .Skip ("This test is skipped because the network is restricted" )
212+ }
213+ return nil
214+ }
0 commit comments