...

Source file src/k8s.io/kubernetes/cmd/kubeadm/app/preflight/checks_test.go

Documentation: k8s.io/kubernetes/cmd/kubeadm/app/preflight

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package preflight
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"net"
    23  	"net/http"
    24  	"os"
    25  	"runtime"
    26  	"strings"
    27  	"testing"
    28  
    29  	utiltesting "k8s.io/client-go/util/testing"
    30  
    31  	"github.com/google/go-cmp/cmp"
    32  	"github.com/lithammer/dedent"
    33  	"github.com/pkg/errors"
    34  
    35  	corev1 "k8s.io/api/core/v1"
    36  	"k8s.io/apimachinery/pkg/util/sets"
    37  	"k8s.io/apimachinery/pkg/util/version"
    38  	"k8s.io/utils/exec"
    39  	fakeexec "k8s.io/utils/exec/testing"
    40  
    41  	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
    42  	kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
    43  	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
    44  	configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
    45  	utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime"
    46  )
    47  
    48  var (
    49  	externalEtcdRootCAFileContent = dedent.Dedent(`
    50  		-----BEGIN CERTIFICATE-----
    51  		MIIFrjCCA5agAwIBAgIUJAM5bQz/Ann8qye8T7Uyl+cAt3wwDQYJKoZIhvcNAQEN
    52  		BQAwbzEOMAwGA1UEBhMFQ2hpbmExDzANBgNVBAgTBkhhaW5hbjEOMAwGA1UEBxMF
    53  		U2FueWExDTALBgNVBAoTBGV0Y2QxFjAUBgNVBAsTDWV0Y2Qgc2VjdXJpdHkxFTAT
    54  		BgNVBAMTDGV0Y2Qtcm9vdC1jYTAeFw0xNzAyMjIwNzEyMDBaFw0yMjAyMjEwNzEy
    55  		MDBaMG8xDjAMBgNVBAYTBUNoaW5hMQ8wDQYDVQQIEwZIYWluYW4xDjAMBgNVBAcT
    56  		BVNhbnlhMQ0wCwYDVQQKEwRldGNkMRYwFAYDVQQLEw1ldGNkIHNlY3VyaXR5MRUw
    57  		EwYDVQQDEwxldGNkLXJvb3QtY2EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
    58  		AoICAQDD16VNTwvEvy1yd/vt8Eq2NwTw51mKHGYlZwsDqdqMEnEiWoJ7Iv9HZ+cl
    59  		jX0FnahKnaV76j3xPO73L5WOvRYxnZ8MvU/aBdDO+Tct4ht3m7TJaav6s55otjDy
    60  		dQNmlpBt4fFEB/nDozQaocfu2mqr5nyKJOjJpe+57Uw4h0LshreDOlzHEs8CkP6W
    61  		/B9yGFARVyz84YgVtemUX8WTB3cVU49KEYMCuhqXY8s97xSTGT/4Tq/MruKb2V+w
    62  		uUPjvyO5eIUcWetjBhgEGsS37NrsSFhoUNMp/PtIkth0LQoWb9sjnG069KIQqm61
    63  		1PKxH7jgLYLf4q455iAuTFr0lF1OcmICTeJB+GiS+3ubOb1TH3AYICXvQUniNWJx
    64  		sDz3qUUu4GLHk9wHtdNmX2FXYB8kHMZAidDM4Zw3IhZZap6n6BlGVVBV5h8sNM3t
    65  		SB+pDLuAaZLx3/ah2ds6AwkfaMdYDsE/MWcWQqzBfhOp758Mx3dF16IY+6IQp0RS
    66  		8qGKxgLDnTF9LgyHVOait2N/pT54faf8//ShSqTqzTK1wzHCkYwL6/B259zXWxeX
    67  		z4gOpQOk4rO4pgm/65QW9aKzHoQnpQ7lFQL2cdsKJv2tyC7pDfVrFy2uHWaUibbP
    68  		7pDw3OD8MQwR1TuhflK1AIicpMQe/kTAuRwH4fneeaGVdddBQQIDAQABo0IwQDAO
    69  		BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUtoqcReNJ
    70  		p8z8Hz1/Q7XMK2fgi74wDQYJKoZIhvcNAQENBQADggIBADbh4HB//Gb0TUUEPoSw
    71  		VMJSUK1pb6KVTqAITSCKPwGT8KfCvVpUxEjh9J3dm1L8wbdr48yffdjhdl96cx2F
    72  		aGWdUIxRBIcpt5xvauBoj0OwfNcD5B9q1aKuh5XPNu4BndNeGw51vdJ8bJbtrZa8
    73  		wKWF/PHciCo/wlzE/YgsemHeY5bYeXawXVP/+ocoLH82Fb8Aq0Af3ZABiA6fmawz
    74  		FiZlnIrZnHVJYSap4yDhC/AQECXKY5gj7kjSnDebsIYds5OrW0D3LeRzs+q5nQXE
    75  		xR35qg834kxUULS8AywqmR3+zjfeymm2FtsjT/PuzEImA80y29qpLZIpPg0meKHF
    76  		pCMJkEHaRh4/JAinLaKCGLpnchqBy7CR6yvVnGkx93J0louIbVyUfn63R6mxCvd7
    77  		kL16a2xBMKgV4RDFcu+VYjbJTFdWOTGFrxPBmd/rLdwD3XNiwPtI0vXGM7I35DDP
    78  		SWwKVvR97F3uEnIQ1u8vHa1pNfQ1qSf/+hUJx2D9ypr7LTQ0LpLh1vUeTeUAVHmT
    79  		EEpcqzDg6lsqXw6KHJ55kd3QR/hRXd/Vr6EWUawDEnGjxyFVV2dTBbunsbSobNI4
    80  		eKV+60oCk3NMwrZoLw4Fv5qs2saS62dgJNfxbKqBX9ljSQxGzHjRwh+hVByCnG8m
    81  		Z9JkQayesM6D7uwbQJXd5rgy
    82  		-----END CERTIFICATE-----
    83  	`)
    84  
    85  	externalEtcdCertFileContent = dedent.Dedent(`
    86  		-----BEGIN CERTIFICATE-----
    87  		MIIGEjCCA/qgAwIBAgIURHJFslbPveA1WwQ4FaPJg1x6B8YwDQYJKoZIhvcNAQEN
    88  		BQAwbzEOMAwGA1UEBhMFQ2hpbmExDzANBgNVBAgTBkhhaW5hbjEOMAwGA1UEBxMF
    89  		U2FueWExDTALBgNVBAoTBGV0Y2QxFjAUBgNVBAsTDWV0Y2Qgc2VjdXJpdHkxFTAT
    90  		BgNVBAMTDGV0Y2Qtcm9vdC1jYTAeFw0xNzAyMjIwNzE0MDBaFw0yNzAyMjAwNzE0
    91  		MDBaMGwxDjAMBgNVBAYTBUNoaW5hMQ8wDQYDVQQIEwZIYWluYW4xDjAMBgNVBAcT
    92  		BVNhbnlhMQ0wCwYDVQQKEwRldGNkMRYwFAYDVQQLEw1ldGNkIHNlY3VyaXR5MRIw
    93  		EAYDVQQDEwlteS1ldGNkLTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
    94  		AQCmCR4OSRrUCES90sUbj5tvjF24lPCMj7qP9MBUxcVvWfaJM12o4AxqBr8OThgd
    95  		lpNvlbKmRpfvbraXiDnuGty1vPa3z7RmKbwFgENfgKHz4fUw/MQ7CALOQ5PAvgf1
    96  		rQ6Ii4cr49nWctpQmBXHtZRjvquBYnw70KrWfQ121DwPYy7cb/StuHLsTgqsgzhl
    97  		ECILWCj9GNqcGQr5+ZvwUxa2yam2CS1M+PLbB6HxX/4RBBTWKAt8+kjt6TxxMaSE
    98  		bNDHNDLWzQSpxg5qTLOQtrubFD4O3JT2E8DEj+LvXJKH7pJd1Z+r0m3ymQvBAIXr
    99  		6OJs+sHbaaxKWS35k9m88NRojR+r5KPoEcBgxhtBtXUfMS5v5dTtcNsHl/mHmTC+
   100  		gWiqpzA+tF55uUEWhRoA+pN7Ie2PviRhG43t99l7bsHVnrxZQqWsWlvCxMN1c2+7
   101  		PRwhsYZFITyKcMSvd19Nb5HGc5hT7btZlWc2xKS2YNnDXbD8C5SdxZek5Cb/xRxL
   102  		T8taf2c1bHs8sZrzIK2DCGvaN3471WEnmaCuRWr2fqyJeCPwsvvWeNDVmgPP6v7g
   103  		ncyy+4QyyfNrdURTZFyw81ZbCiznPc070u7vtIYt3Sa0NXd0oEG1ybAZwBIYhMOY
   104  		5ctepJLf7QxHXR70RdI0ksHEmZGZ1igk7gzhmHEgQM87pQIDAQABo4GoMIGlMA4G
   105  		A1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYD
   106  		VR0TAQH/BAIwADAdBgNVHQ4EFgQU0U/Zn4mc95UXm+LVO67wqJpL9gIwHwYDVR0j
   107  		BBgwFoAUtoqcReNJp8z8Hz1/Q7XMK2fgi74wJgYDVR0RBB8wHYIJbG9jYWxob3N0
   108  		hwR/AAABhwQKcjPGhwQKcgwwMA0GCSqGSIb3DQEBDQUAA4ICAQCikW5SNpndBxEz
   109  		qblER72KkfSEXMFhQry3RZJeAw6rQiOl+PMJqMnylcepOAUrNi20emS270dQDh3z
   110  		Hw/JBgKftZ1JrjbF9NF4oFUZcFUKmTgyWYnhLH0BskgwJf2u+DpugFa4U8niQf15
   111  		ciZGoUfWCGOJbgVP7esdnyhH/P/DpOEObWf8vOfvfQ49r7MzATyzMESyJjdtAH/F
   112  		c5JKACxpJhaYfTZ78F43jSw0vswBdLQ7fJWqg/sJBlTG0GBFJcEJzFVpwzYUxwZ4
   113  		rUpAn4A02M2V9XDNlptrWvcQz/5Vs/aCmehz7GOiMJB6SLWcMSpJRLMqoJjaFVfO
   114  		OPm7bWMMaVOUPedzvcBKRXmEAg7HQnm3ibkVNjTW8Hr66n34Yk/dO9WXD+6IXnOQ
   115  		bMY+Mf9vpIsscSpGTO15sAKqiXCzHR9RWqNd4U3jvo3JtewkNMhIKzPThgYNfsO3
   116  		7HSrlfffeEQKc59rDUaC3Y9YSc5ERJRMC+mdOqXNMy2iedZnNEsmgYlaVDg6xfG8
   117  		65w9UkMOe+DTJtMHnMxP4rT6WE4cKysQeSYxkyo/jh+8rKEy9+AyuEntJAknABUc
   118  		N5mizdYu8nrtiSu9jdLKhwO41gC2IlXPUHizylo6g24RFVBjHLlzYAAsVMMMSQW1
   119  		XRMVQjawUTknbAgHuE7/rEX8c27WUA==
   120  		-----END CERTIFICATE-----
   121  	`)
   122  	externalEtcdKeyFileContent = dedent.Dedent(`
   123  		-----BEGIN RSA PRIVATE KEY-----
   124  		MIIJKAIBAAKCAgEApgkeDkka1AhEvdLFG4+bb4xduJTwjI+6j/TAVMXFb1n2iTNd
   125  		qOAMaga/Dk4YHZaTb5WypkaX7262l4g57hrctbz2t8+0Zim8BYBDX4Ch8+H1MPzE
   126  		OwgCzkOTwL4H9a0OiIuHK+PZ1nLaUJgVx7WUY76rgWJ8O9Cq1n0NdtQ8D2Mu3G/0
   127  		rbhy7E4KrIM4ZRAiC1go/RjanBkK+fmb8FMWtsmptgktTPjy2weh8V/+EQQU1igL
   128  		fPpI7ek8cTGkhGzQxzQy1s0EqcYOakyzkLa7mxQ+DtyU9hPAxI/i71ySh+6SXdWf
   129  		q9Jt8pkLwQCF6+jibPrB22msSlkt+ZPZvPDUaI0fq+Sj6BHAYMYbQbV1HzEub+XU
   130  		7XDbB5f5h5kwvoFoqqcwPrReeblBFoUaAPqTeyHtj74kYRuN7ffZe27B1Z68WUKl
   131  		rFpbwsTDdXNvuz0cIbGGRSE8inDEr3dfTW+RxnOYU+27WZVnNsSktmDZw12w/AuU
   132  		ncWXpOQm/8UcS0/LWn9nNWx7PLGa8yCtgwhr2jd+O9VhJ5mgrkVq9n6siXgj8LL7
   133  		1njQ1ZoDz+r+4J3MsvuEMsnza3VEU2RcsPNWWwos5z3NO9Lu77SGLd0mtDV3dKBB
   134  		tcmwGcASGITDmOXLXqSS3+0MR10e9EXSNJLBxJmRmdYoJO4M4ZhxIEDPO6UCAwEA
   135  		AQKCAgEAmr3OlDPP3CLkpiFEcJ5TmA+y3S96TRY7IqVRhvBXRKMMoOwNczF0gHBP
   136  		Ka7gzNqkCA/1UwBh49VEOU/N5bqFTp+RNNhQYhKtWFck82H4Dkrd8EzzOa0KqF/U
   137  		2YKB+pbR/7JCRUZypGmgTBKh4eG6LYfrYYd/D2Q3g/VCUigU3aZrayiwWiOYf+Fw
   138  		Ez2slowFnpsIgHHkdCzmzPi0O7PEbJDgKXa+EInIFRg09renGwa5wKnLoyvEQm7o
   139  		VPqWQJEFt1JPu1+R5ARhNPLNO6cCi9K+z60G65yXQNp0/u5A5o0TPn609DcHH11B
   140  		1ht9tNL0C+tcNvhyiUw6C+uet3egDVu1TqptzAfb2Y3MQK6UV/by7KJxcFxBAzWl
   141  		UQ4zDaQzCcU81T92zI+XeRSJuCPuOL61mH7zEiPZZPOLV8MbxBX/7lj+IJTBL+vJ
   142  		Idq7Nn/+LRtuSy5PH2MzZ5DzIMmjkjQ/ScpzAr9Zpkm3dpTcGTpFV0uqHseE77Re
   143  		55tz9uB7pxV1n6Gz4uMNnsioEYsFIRfzst4QWDdaQqcYJQuKvW9pXNmgRgSCIlft
   144  		54DxQ98a1PVFmS40TT9mjUg0P66m+8bk5vEb58iAjoYJRcoriZhlT6cOcuPW6hos
   145  		3PfA2gMXuWu61mAjzdP0zbzNBXCn5nRppqLNmWMVZCI0nLjmyZUCggEBAMEpCQu9
   146  		cRWc/GjvmnfXHewvqQHu3A3J1HCLR0VqJo8rcIIvhSe7dPRAMtUFxV1R2eOfMvSZ
   147  		Y4y69tMHZPVTgnp2t5TSavjpMqSQLvXyBkgL8FnGEl5l6HEQTm8y0C13Cm+CUB5a
   148  		uxQnQflkX539SjWX0XdOmYuLORmrKGxgcDOd9652fDJcFSXYa0mx6KN2JZHh9psA
   149  		9ldHhUIq1ngoVnrctlK53MptckPrFwMFdXRCKiMfkvpUkXTeXu4D7Z1VNh2V/3gF
   150  		lmRNioXaxp7W8omBSQlwaHY5btPj5jktiC9/so4ORqJjHvbCURrIvdkPPaXi/YJy
   151  		HdoOgHYFnn3p6M8CggEBANwNDtdbHWwwVC7Op6TNc8qK+SWAId5RqPOmM70XBVvg
   152  		u9nxT7a5vmRTs81fcVoxtE0t+KWIfOXquxqTbk0ONqIsl2CLTiTFaNHoHlvwgFBT
   153  		aYukORiGILIzOJr82RPugAw1+j8jmw3OsCOXnf2odGs+oC/V9vEd9NyZpDHPohtK
   154  		a8Bk8p326mQam23ArUesIqnw31fG22KRpoLXuk/9nNcAAAZd1Qd9hGWf0HHxunXB
   155  		wj6e3VTm0G4NPTli5vmVavYRPMFUUJpU5lwTHhlrHTSmANHTjZGnn0mEOfIrfodF
   156  		ODwJjwoyq4rPls0fqOvyAyBCnhop4fC8yOd4cQcLSUsCggEAbv9Br3lhLmZTtYla
   157  		XltDWqHYoL+9vD6q0TF39y+UkNkJggYEolxaTLFHhJoYXBPY/bBR+7TZO9mEVKf/
   158  		H+qpI+5seByiU/7NlzszgSle6q/RogTsMUqmU7JnIAc3EalCWemsWIUS0/XrN4Cy
   159  		YXtX1Yw0VjbYjROn8FQmmoCgeUjhN2Pm4pl/nYvLu0F8ydHurPIIX/IhnO4AaZFs
   160  		RQgJCfki3E7pzXkvHFBPnPDaGcCbritKrodCPsI6EtQ3Cx4YRtAXScUMMv9MBrc9
   161  		Q7GJFfMxITdzD9zZDvH7Lgg4JfNfi7owZMhI1su7B4UrczwK1PSncPpapR+IOkno
   162  		VbrAiQKCAQB2xGV6PqdGuV72VHuPK4SPkSqf3uRoxdJWjyHlsQMnb8hz/RZ1HRNx
   163  		uuuUsSrQ73rNHT7SuTQQM/0AfwpNdJpwNXkOlqF6n0HP6WRZYxkeQab5w409e0cy
   164  		ZwrqPAY+B7/81zVV1rXdYe0XiMGxIraTG54Bs44w3WZHmnVQnSx1Zll54gJA1//y
   165  		P5ocRp4/zNx4tJUXHzFRpiMlA6J/gfag5FMfHI3aGRjYcMVken+VBxr8CWqUZG+i
   166  		tmqRCpx3oPm2Dd+oyQUoByK+F2NrfLCqtd5DYddLAhmq6D8OQgNspyOO4+ncKzUD
   167  		Gr/dvnTBxEGDq/EBVhGoiXw10n/OuXy5AoIBAAUAoTyt4gQjjC0ddtMLN7+R1Ymp
   168  		eNULpq2XTvidj7jaysIW9Q52ncvN6h2Vds/Z3Ujvdne2jMq7Q/C96fKcrhgMH9ca
   169  		ADGLWtD+VkP4NgFjj7R2jabF8d9IQdJDXAgvR/kokojF0RsJuvD2hawN6lQkkj6S
   170  		fNNGMBk4sGyt7gzAn3iO4Zoy+QjtALNnZcaH6s7oIg3UKf6OwskiBB60Q5P1U3/E
   171  		RPtTxhex3jFuySNJ413JgyGkvcP+qjuzi6eyVDxkfiyNohQYGuZ8rieFX7QfQFAY
   172  		TIXptchVUTxmGKWzcpLC3AfkwFvV2IPoMk8YnDSp270D30cqWiI9puSEcxQ=
   173  		-----END RSA PRIVATE KEY-----
   174  	`)
   175  )
   176  
   177  type preflightCheckTest struct {
   178  	msg string
   179  }
   180  
   181  func (pfct preflightCheckTest) Name() string {
   182  	return "preflightCheckTest"
   183  }
   184  
   185  func (pfct preflightCheckTest) Check() (warning, errorList []error) {
   186  	if pfct.msg == "warning" {
   187  		return []error{errors.New("warning")}, nil
   188  	}
   189  	if pfct.msg != "" {
   190  		return nil, []error{errors.New("fake error")}
   191  	}
   192  	return
   193  }
   194  
   195  func TestFileExistingCheck(t *testing.T) {
   196  	f, err := os.CreateTemp("", "file-exist-check")
   197  	if err != nil {
   198  		t.Fatalf("Failed to create file: %v", err)
   199  	}
   200  	defer utiltesting.CloseAndRemove(t, f)
   201  	var tests = []struct {
   202  		name          string
   203  		check         FileExistingCheck
   204  		expectedError bool
   205  	}{
   206  		{
   207  			name: "File does not exist, so it's not available",
   208  			check: FileExistingCheck{
   209  				Path: "/does/not/exist",
   210  			},
   211  			expectedError: true,
   212  		},
   213  		{
   214  			name: "File exists and is available",
   215  			check: FileExistingCheck{
   216  				Path: f.Name(),
   217  			},
   218  			expectedError: false,
   219  		},
   220  	}
   221  	for _, rt := range tests {
   222  		_, output := rt.check.Check()
   223  		if (output != nil) != rt.expectedError {
   224  			t.Errorf(
   225  				"Failed FileExistingCheck:%v\n\texpectedError: %t\n\t  actual: %t",
   226  				rt.name,
   227  				rt.expectedError,
   228  				(output != nil),
   229  			)
   230  		}
   231  	}
   232  }
   233  
   234  func TestFileAvailableCheck(t *testing.T) {
   235  	f, err := os.CreateTemp("", "file-avail-check")
   236  	if err != nil {
   237  		t.Fatalf("Failed to create file: %v", err)
   238  	}
   239  	defer utiltesting.CloseAndRemove(t, f)
   240  	var tests = []struct {
   241  		name          string
   242  		check         FileAvailableCheck
   243  		expectedError bool
   244  	}{
   245  		{
   246  			name: "The file does not exist",
   247  			check: FileAvailableCheck{
   248  				Path: "/does/not/exist",
   249  			},
   250  			expectedError: false,
   251  		},
   252  		{
   253  			name: "The file exists",
   254  			check: FileAvailableCheck{
   255  				Path: f.Name(),
   256  			},
   257  			expectedError: true,
   258  		},
   259  	}
   260  	for _, rt := range tests {
   261  		_, output := rt.check.Check()
   262  		if (output != nil) != rt.expectedError {
   263  			t.Errorf(
   264  				"Failed FileAvailableCheck:%v\n\texpectedError: %t\n\t  actual: %t",
   265  				rt.name,
   266  				rt.expectedError,
   267  				(output != nil),
   268  			)
   269  		}
   270  	}
   271  }
   272  
   273  func TestFileContentCheck(t *testing.T) {
   274  	f, err := os.CreateTemp("", "file-content-check")
   275  	if err != nil {
   276  		t.Fatalf("Failed to create file: %v", err)
   277  	}
   278  	defer os.Remove(f.Name())
   279  	var tests = []struct {
   280  		name          string
   281  		check         FileContentCheck
   282  		expectedError bool
   283  	}{
   284  		{
   285  			name: "File exists and has matching content",
   286  			check: FileContentCheck{
   287  				Path:    f.Name(),
   288  				Content: []byte("Test FileContentCheck"),
   289  			},
   290  			expectedError: false,
   291  		},
   292  		{
   293  			name: "File exists, content is nil",
   294  			check: FileContentCheck{
   295  				Path:    f.Name(),
   296  				Content: nil,
   297  			},
   298  			expectedError: false,
   299  		},
   300  		{
   301  			name: "File exists but has unexpected content",
   302  			check: FileContentCheck{
   303  				Path:    f.Name(),
   304  				Content: []byte("foo"),
   305  			},
   306  			expectedError: true,
   307  		},
   308  		{
   309  			name: "File does not exist, content is not nil",
   310  			check: FileContentCheck{
   311  				Path:    "/does/not/exist",
   312  				Content: []byte("foo"),
   313  			},
   314  			expectedError: true,
   315  		},
   316  		{
   317  			name: "File dose not exist, content is nil",
   318  			check: FileContentCheck{
   319  				Path:    "/does/not/exist",
   320  				Content: nil,
   321  			},
   322  			expectedError: true,
   323  		},
   324  	}
   325  	if _, err = f.WriteString("Test FileContentCheck"); err != nil {
   326  		t.Fatalf("Failed to write to file: %v", err)
   327  	}
   328  	for _, rt := range tests {
   329  		_, output := rt.check.Check()
   330  		if (len(output) > 0) != rt.expectedError {
   331  			t.Errorf(
   332  				"Failed FileContentCheck:%v\n\texpectedError: %t\n\t  actual: %t",
   333  				rt.name,
   334  				rt.expectedError,
   335  				(len(output) > 0),
   336  			)
   337  		}
   338  	}
   339  }
   340  
   341  func TestDirAvailableCheck(t *testing.T) {
   342  	fileDir, err := os.MkdirTemp("", "dir-avail-check")
   343  	if err != nil {
   344  		t.Fatalf("failed creating directory: %v", err)
   345  	}
   346  	defer os.RemoveAll(fileDir)
   347  	var tests = []struct {
   348  		name          string
   349  		check         DirAvailableCheck
   350  		expectedError bool
   351  	}{
   352  		{
   353  			name: "Directory exists and is empty",
   354  			check: DirAvailableCheck{
   355  				Path: fileDir,
   356  			},
   357  			expectedError: false,
   358  		},
   359  		{
   360  			name: "Directory exists and has something",
   361  			check: DirAvailableCheck{
   362  				Path: os.TempDir(), // a directory was created previously in this test
   363  			},
   364  			expectedError: true,
   365  		},
   366  		{
   367  			name: "Directory does not exist",
   368  			check: DirAvailableCheck{
   369  				Path: "/does/not/exist",
   370  			},
   371  			expectedError: false,
   372  		},
   373  	}
   374  	for _, rt := range tests {
   375  		_, output := rt.check.Check()
   376  		if (output != nil) != rt.expectedError {
   377  			t.Errorf(
   378  				"Failed DirAvailableCheck:%v\n\texpectedError: %t\n\t  actual: %t",
   379  				rt.name,
   380  				rt.expectedError,
   381  				(output != nil),
   382  			)
   383  		}
   384  	}
   385  }
   386  
   387  func TestPortOpenCheck(t *testing.T) {
   388  	ln, err := net.Listen("tcp", ":0")
   389  	if err != nil {
   390  		t.Fatalf("could not listen on local network: %v", err)
   391  	}
   392  	defer ln.Close()
   393  	var tests = []struct {
   394  		name          string
   395  		check         PortOpenCheck
   396  		expectedError bool
   397  	}{
   398  		{
   399  			name:          "Port is available",
   400  			check:         PortOpenCheck{port: 0},
   401  			expectedError: false,
   402  		},
   403  		{
   404  			name:          "Port is not available",
   405  			check:         PortOpenCheck{port: ln.Addr().(*net.TCPAddr).Port},
   406  			expectedError: true,
   407  		},
   408  	}
   409  	for _, rt := range tests {
   410  		_, output := rt.check.Check()
   411  		if (output != nil) != rt.expectedError {
   412  			t.Errorf(
   413  				"Failed PortOpenCheck:%v\n\texpectedError: %t\n\t  actual: %t",
   414  				rt.name,
   415  				rt.expectedError,
   416  				(output != nil),
   417  			)
   418  		}
   419  	}
   420  }
   421  
   422  func TestRunChecks(t *testing.T) {
   423  	var tokenTest = []struct {
   424  		p        []Checker
   425  		expected bool
   426  		output   string
   427  	}{
   428  		{[]Checker{}, true, ""},
   429  		{[]Checker{preflightCheckTest{"warning"}}, true, "\t[WARNING preflightCheckTest]: warning\n"}, // should just print warning
   430  		{[]Checker{preflightCheckTest{"error"}}, false, ""},
   431  		{[]Checker{preflightCheckTest{"test"}}, false, ""},
   432  		{[]Checker{DirAvailableCheck{Path: "/does/not/exist"}}, true, ""},
   433  		{[]Checker{DirAvailableCheck{Path: "/"}}, false, ""},
   434  		{[]Checker{FileAvailableCheck{Path: "/does/not/exist"}}, true, ""},
   435  		{[]Checker{FileContentCheck{Path: "/does/not/exist"}}, false, ""},
   436  		{[]Checker{FileContentCheck{Path: "/"}}, true, ""},
   437  		{[]Checker{FileContentCheck{Path: "/", Content: []byte("does not exist")}}, false, ""},
   438  		{[]Checker{InPathCheck{executable: "foobarbaz", exec: exec.New()}}, true, "\t[WARNING FileExisting-foobarbaz]: foobarbaz not found in system path\n"},
   439  		{[]Checker{InPathCheck{executable: "foobarbaz", mandatory: true, exec: exec.New()}}, false, ""},
   440  		{[]Checker{InPathCheck{executable: "foobar", mandatory: false, exec: exec.New(), suggestion: "install foobar"}}, true, "\t[WARNING FileExisting-foobar]: foobar not found in system path\nSuggestion: install foobar\n"},
   441  	}
   442  	for _, rt := range tokenTest {
   443  		buf := new(bytes.Buffer)
   444  		actual := RunChecks(rt.p, buf, sets.New[string]())
   445  		if (actual == nil) != rt.expected {
   446  			t.Errorf(
   447  				"failed RunChecks:\n\texpected: %t\n\t  actual: %t",
   448  				rt.expected,
   449  				(actual == nil),
   450  			)
   451  		}
   452  		if buf.String() != rt.output {
   453  			t.Errorf(
   454  				"failed RunChecks:\n\texpected: %s\n\t  actual: %s",
   455  				rt.output,
   456  				buf.String(),
   457  			)
   458  		}
   459  	}
   460  }
   461  func TestConfigRootCAs(t *testing.T) {
   462  	f, err := os.CreateTemp(os.TempDir(), "kubeadm-external-etcd-test-cafile")
   463  	if err != nil {
   464  		t.Errorf("failed configRootCAs:\n\texpected: succeed creating temp CA file\n\tactual:%v", err)
   465  	}
   466  	defer utiltesting.CloseAndRemove(t, f)
   467  	if _, err := f.Write([]byte(externalEtcdRootCAFileContent)); err != nil {
   468  		t.Errorf("failed configRootCAs:\n\texpected: succeed writing contents to temp CA file %s\n\tactual:%v", f.Name(), err)
   469  	}
   470  
   471  	c := ExternalEtcdVersionCheck{Etcd: kubeadmapi.Etcd{External: &kubeadmapi.ExternalEtcd{CAFile: f.Name()}}}
   472  
   473  	config, err := c.configRootCAs(nil)
   474  	if err != nil {
   475  		t.Errorf(
   476  			"failed configRootCAs:\n\texpected: has no error\n\tactual:%v",
   477  			err,
   478  		)
   479  	}
   480  	if config.RootCAs == nil {
   481  		t.Errorf(
   482  			"failed configRootCAs:\n\texpected: RootCAs not equal to nil\n\tactual:%v",
   483  			config.RootCAs,
   484  		)
   485  	}
   486  }
   487  func TestConfigCertAndKey(t *testing.T) {
   488  	certFile, err := os.CreateTemp(os.TempDir(), "kubeadm-external-etcd-test-certfile")
   489  	if err != nil {
   490  		t.Errorf(
   491  			"failed configCertAndKey:\n\texpected: succeed creating temp CertFile file\n\tactual:%v",
   492  			err,
   493  		)
   494  	}
   495  	defer utiltesting.CloseAndRemove(t, certFile)
   496  	if _, err := certFile.Write([]byte(externalEtcdCertFileContent)); err != nil {
   497  		t.Errorf(
   498  			"failed configCertAndKey:\n\texpected: succeed writing contents to temp CertFile file %s\n\tactual:%v",
   499  			certFile.Name(),
   500  			err,
   501  		)
   502  	}
   503  
   504  	keyFile, err := os.CreateTemp(os.TempDir(), "kubeadm-external-etcd-test-keyfile")
   505  	if err != nil {
   506  		t.Errorf(
   507  			"failed configCertAndKey:\n\texpected: succeed creating temp KeyFile file\n\tactual:%v",
   508  			err,
   509  		)
   510  	}
   511  	defer utiltesting.CloseAndRemove(t, keyFile)
   512  	if _, err := keyFile.Write([]byte(externalEtcdKeyFileContent)); err != nil {
   513  		t.Errorf(
   514  			"failed configCertAndKey:\n\texpected: succeed writing contents to temp KeyFile file %s\n\tactual:%v",
   515  			keyFile.Name(),
   516  			err,
   517  		)
   518  	}
   519  	c := ExternalEtcdVersionCheck{
   520  		Etcd: kubeadmapi.Etcd{
   521  			External: &kubeadmapi.ExternalEtcd{
   522  				CertFile: certFile.Name(),
   523  				KeyFile:  keyFile.Name(),
   524  			},
   525  		},
   526  	}
   527  
   528  	config, err := c.configCertAndKey(nil)
   529  	if err != nil {
   530  		t.Errorf(
   531  			"failed configCertAndKey:\n\texpected: has no error\n\tactual:%v",
   532  			err,
   533  		)
   534  	}
   535  	if config.Certificates == nil {
   536  		t.Errorf(
   537  			"failed configCertAndKey:\n\texpected: Certificates not equal to nil\n\tactual:%v",
   538  			config.Certificates,
   539  		)
   540  	}
   541  }
   542  
   543  func TestKubernetesVersionCheck(t *testing.T) {
   544  	var tests = []struct {
   545  		check          KubernetesVersionCheck
   546  		expectWarnings bool
   547  	}{
   548  		{
   549  			check: KubernetesVersionCheck{
   550  				KubeadmVersion:    "v1.6.6", //Same version
   551  				KubernetesVersion: "v1.6.6",
   552  			},
   553  			expectWarnings: false,
   554  		},
   555  		{
   556  			check: KubernetesVersionCheck{
   557  				KubeadmVersion:    "v1.6.6", //KubernetesVersion version older than KubeadmVersion
   558  				KubernetesVersion: "v1.5.5",
   559  			},
   560  			expectWarnings: false,
   561  		},
   562  		{
   563  			check: KubernetesVersionCheck{
   564  				KubeadmVersion:    "v1.6.6", //KubernetesVersion newer than KubeadmVersion, within the same minor release (new patch)
   565  				KubernetesVersion: "v1.6.7",
   566  			},
   567  			expectWarnings: false,
   568  		},
   569  		{
   570  			check: KubernetesVersionCheck{
   571  				KubeadmVersion:    "v1.6.6", //KubernetesVersion newer than KubeadmVersion, in a different minor/in pre-release
   572  				KubernetesVersion: "v1.7.0-alpha.0",
   573  			},
   574  			expectWarnings: true,
   575  		},
   576  		{
   577  			check: KubernetesVersionCheck{
   578  				KubeadmVersion:    "v1.6.6", //KubernetesVersion newer than KubeadmVersion, in a different minor/stable
   579  				KubernetesVersion: "v1.7.0",
   580  			},
   581  			expectWarnings: true,
   582  		},
   583  		{
   584  			check: KubernetesVersionCheck{
   585  				KubeadmVersion:    "v0.0.0", //"super-custom" builds - Skip this check
   586  				KubernetesVersion: "v1.7.0",
   587  			},
   588  			expectWarnings: false,
   589  		},
   590  	}
   591  
   592  	for _, rt := range tests {
   593  		warning, _ := rt.check.Check()
   594  		if (warning != nil) != rt.expectWarnings {
   595  			t.Errorf(
   596  				"failed KubernetesVersionCheck:\n\texpected: %t\n\t  actual: %t (KubeadmVersion:%s, KubernetesVersion: %s)",
   597  				rt.expectWarnings,
   598  				(warning != nil),
   599  				rt.check.KubeadmVersion,
   600  				rt.check.KubernetesVersion,
   601  			)
   602  		}
   603  	}
   604  }
   605  
   606  func TestHTTPProxyCIDRCheck(t *testing.T) {
   607  	var tests = []struct {
   608  		check          HTTPProxyCIDRCheck
   609  		expectWarnings bool
   610  	}{
   611  		{
   612  			check: HTTPProxyCIDRCheck{
   613  				Proto: "https",
   614  				CIDR:  "127.0.0.0/8",
   615  			}, // Loopback addresses never should produce proxy warnings
   616  			expectWarnings: false,
   617  		},
   618  		{
   619  			check: HTTPProxyCIDRCheck{
   620  				Proto: "https",
   621  				CIDR:  "10.96.0.0/12",
   622  			}, // Expected to be accessed directly, we set NO_PROXY to 10.0.0.0/8
   623  			expectWarnings: false,
   624  		},
   625  		{
   626  			check: HTTPProxyCIDRCheck{
   627  				Proto: "https",
   628  				CIDR:  "192.168.0.0/16",
   629  			}, // Expected to go via proxy as this range is not listed in NO_PROXY
   630  			expectWarnings: true,
   631  		},
   632  		{
   633  			check: HTTPProxyCIDRCheck{
   634  				Proto: "https",
   635  				CIDR:  "2001:db8::/56",
   636  			}, // Expected to be accessed directly, part of 2001:db8::/48 in NO_PROXY
   637  			expectWarnings: false,
   638  		},
   639  		{
   640  			check: HTTPProxyCIDRCheck{
   641  				Proto: "https",
   642  				CIDR:  "2001:db8:1::/56",
   643  			}, // Expected to go via proxy, range is not in 2001:db8::/48
   644  			expectWarnings: true,
   645  		},
   646  	}
   647  
   648  	resetProxyEnv(t)
   649  
   650  	for _, rt := range tests {
   651  		warning, _ := rt.check.Check()
   652  		if (warning != nil) != rt.expectWarnings {
   653  			t.Errorf(
   654  				"failed HTTPProxyCIDRCheck:\n\texpected: %t\n\t  actual: %t (CIDR:%s). Warnings: %v",
   655  				rt.expectWarnings,
   656  				(warning != nil),
   657  				rt.check.CIDR,
   658  				warning,
   659  			)
   660  		}
   661  	}
   662  }
   663  
   664  func TestHTTPProxyCheck(t *testing.T) {
   665  	var tests = []struct {
   666  		name           string
   667  		check          HTTPProxyCheck
   668  		expectWarnings bool
   669  	}{
   670  		{
   671  			name: "Loopback address",
   672  			check: HTTPProxyCheck{
   673  				Proto: "https",
   674  				Host:  "127.0.0.1",
   675  			}, // Loopback addresses never should produce proxy warnings
   676  			expectWarnings: false,
   677  		},
   678  		{
   679  			name: "IPv4 direct access",
   680  			check: HTTPProxyCheck{
   681  				Proto: "https",
   682  				Host:  "10.96.0.1",
   683  			}, // Expected to be accessed directly, we set NO_PROXY to 10.0.0.0/8
   684  			expectWarnings: false,
   685  		},
   686  		{
   687  			name: "IPv4 via proxy",
   688  			check: HTTPProxyCheck{
   689  				Proto: "https",
   690  				Host:  "192.168.0.1",
   691  			}, // Expected to go via proxy as this range is not listed in NO_PROXY
   692  			expectWarnings: true,
   693  		},
   694  		{
   695  			name: "IPv6 direct access",
   696  			check: HTTPProxyCheck{
   697  				Proto: "https",
   698  				Host:  "[2001:db8::1:15]",
   699  			}, // Expected to be accessed directly, part of 2001:db8::/48 in NO_PROXY
   700  			expectWarnings: false,
   701  		},
   702  		{
   703  			name: "IPv6 via proxy",
   704  			check: HTTPProxyCheck{
   705  				Proto: "https",
   706  				Host:  "[2001:db8:1::1:15]",
   707  			}, // Expected to go via proxy, range is not in 2001:db8::/48
   708  			expectWarnings: true,
   709  		},
   710  		{
   711  			name: "IPv6 direct access, no brackets",
   712  			check: HTTPProxyCheck{
   713  				Proto: "https",
   714  				Host:  "2001:db8::1:15",
   715  			}, // Expected to be accessed directly, part of 2001:db8::/48 in NO_PROXY
   716  			expectWarnings: false,
   717  		},
   718  		{
   719  			name: "IPv6 via proxy, no brackets",
   720  			check: HTTPProxyCheck{
   721  				Proto: "https",
   722  				Host:  "2001:db8:1::1:15",
   723  			}, // Expected to go via proxy, range is not in 2001:db8::/48
   724  			expectWarnings: true,
   725  		},
   726  	}
   727  
   728  	resetProxyEnv(t)
   729  
   730  	for _, rt := range tests {
   731  		warning, _ := rt.check.Check()
   732  		if (warning != nil) != rt.expectWarnings {
   733  			t.Errorf(
   734  				"%s failed HTTPProxyCheck:\n\texpected: %t\n\t  actual: %t (Host:%s). Warnings: %v",
   735  				rt.name,
   736  				rt.expectWarnings,
   737  				(warning != nil),
   738  				rt.check.Host,
   739  				warning,
   740  			)
   741  		}
   742  	}
   743  }
   744  
   745  // resetProxyEnv is helper function that unsets all *_proxy variables.
   746  func resetProxyEnv(t *testing.T) {
   747  	for _, e := range os.Environ() {
   748  		key, value, _ := strings.Cut(e, "=")
   749  		if strings.HasSuffix(strings.ToLower(key), "_proxy") {
   750  			t.Cleanup(func() { os.Setenv(key, value) })
   751  			os.Unsetenv(key)
   752  		}
   753  	}
   754  
   755  	t.Setenv("HTTP_PROXY", "http://proxy.example.com:3128")
   756  	t.Setenv("HTTPS_PROXY", "https://proxy.example.com:3128")
   757  	t.Setenv("NO_PROXY", "example.com,10.0.0.0/8,2001:db8::/48")
   758  	// Check if we can reliably execute tests:
   759  	// ProxyFromEnvironment caches the *_proxy environment variables and
   760  	// if ProxyFromEnvironment already executed before our test with empty
   761  	// HTTP_PROXY it will make these tests return false positive failures
   762  	req, err := http.NewRequest("GET", "http://host.fake.tld/", nil)
   763  	if err != nil {
   764  		t.Fatalf("unexpected err: %v", err)
   765  	}
   766  	proxy, err := http.ProxyFromEnvironment(req)
   767  	if err != nil {
   768  		t.Fatalf("unexpected err: %v", err)
   769  	}
   770  	if proxy == nil {
   771  		t.Skip("test skipped as ProxyFromEnvironment already initialized in environment without defined HTTP proxy")
   772  	}
   773  	t.Log("http.ProxyFromEnvironment is usable, continue executing test")
   774  }
   775  
   776  func TestKubeletVersionCheck(t *testing.T) {
   777  	minimumKubeletVersion := version.MustParseSemantic("v1.3.0")
   778  	minimumControlPlaneVersion := version.MustParseSemantic("v1.3.0")
   779  	currentKubernetesVersion := version.MustParseSemantic("v1.4.0")
   780  	cases := []struct {
   781  		kubeletVersion string
   782  		k8sVersion     string
   783  		expectErrors   bool
   784  		expectWarnings bool
   785  	}{
   786  		{"v" + currentKubernetesVersion.WithPatch(2).String(), "", false, false},                                                           // check minimally supported version when there is no information about control plane
   787  		{"v1.1.0", "v1.11.8", true, false},                                                                                                 // too old kubelet, should fail.
   788  		{"v" + minimumKubeletVersion.String(), minimumControlPlaneVersion.WithPatch(5).String(), false, false},                             // kubelet within same major.minor as control plane
   789  		{"v" + minimumKubeletVersion.WithPatch(5).String(), minimumControlPlaneVersion.WithPatch(1).String(), false, false},                // kubelet is newer, but still within same major.minor as control plane
   790  		{"v" + minimumKubeletVersion.String(), currentKubernetesVersion.WithPatch(1).String(), false, false},                               // kubelet is lower than control plane, but newer than minimally supported
   791  		{"v" + currentKubernetesVersion.WithPreRelease("alpha.1").String(), minimumControlPlaneVersion.WithPatch(1).String(), true, false}, // kubelet is newer (development build) than control plane, should fail.
   792  		{"v" + currentKubernetesVersion.String(), minimumControlPlaneVersion.WithPatch(5).String(), true, false},                           // kubelet is newer (release) than control plane, should fail.
   793  	}
   794  
   795  	for _, tc := range cases {
   796  		t.Run(tc.kubeletVersion, func(t *testing.T) {
   797  			fcmd := fakeexec.FakeCmd{
   798  				OutputScript: []fakeexec.FakeAction{
   799  					func() ([]byte, []byte, error) { return []byte("Kubernetes " + tc.kubeletVersion), nil, nil },
   800  				},
   801  			}
   802  			fexec := &fakeexec.FakeExec{
   803  				CommandScript: []fakeexec.FakeCommandAction{
   804  					func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   805  				},
   806  			}
   807  
   808  			check := KubeletVersionCheck{KubernetesVersion: tc.k8sVersion, exec: fexec, minKubeletVersion: minimumKubeletVersion}
   809  			warnings, errors := check.Check()
   810  
   811  			switch {
   812  			case warnings != nil && !tc.expectWarnings:
   813  				t.Errorf("KubeletVersionCheck: unexpected warnings for kubelet version %q and Kubernetes version %q. Warnings: %v", tc.kubeletVersion, tc.k8sVersion, warnings)
   814  			case warnings == nil && tc.expectWarnings:
   815  				t.Errorf("KubeletVersionCheck: expected warnings for kubelet version %q and Kubernetes version %q but got nothing", tc.kubeletVersion, tc.k8sVersion)
   816  			case errors != nil && !tc.expectErrors:
   817  				t.Errorf("KubeletVersionCheck: unexpected errors for kubelet version %q and Kubernetes version %q. errors: %v", tc.kubeletVersion, tc.k8sVersion, errors)
   818  			case errors == nil && tc.expectErrors:
   819  				t.Errorf("KubeletVersionCheck: expected errors for kubelet version %q and Kubernetes version %q but got nothing", tc.kubeletVersion, tc.k8sVersion)
   820  			}
   821  		})
   822  	}
   823  }
   824  
   825  func TestSetHasItemOrAll(t *testing.T) {
   826  	var tests = []struct {
   827  		ignoreSet      sets.Set[string]
   828  		testString     string
   829  		expectedResult bool
   830  	}{
   831  		{sets.New[string](), "foo", false},
   832  		{sets.New("all"), "foo", true},
   833  		{sets.New("all", "bar"), "foo", true},
   834  		{sets.New("bar"), "foo", false},
   835  		{sets.New("baz", "foo", "bar"), "foo", true},
   836  		{sets.New("baz", "bar", "foo"), "Foo", true},
   837  	}
   838  
   839  	for _, rt := range tests {
   840  		t.Run(rt.testString, func(t *testing.T) {
   841  			result := setHasItemOrAll(rt.ignoreSet, rt.testString)
   842  			if result != rt.expectedResult {
   843  				t.Errorf(
   844  					"setHasItemOrAll: expected: %v actual: %v (arguments: %q, %q)",
   845  					rt.expectedResult, result,
   846  					rt.ignoreSet,
   847  					rt.testString,
   848  				)
   849  			}
   850  		})
   851  	}
   852  }
   853  
   854  func TestImagePullCheck(t *testing.T) {
   855  	fcmd := fakeexec.FakeCmd{
   856  		RunScript: []fakeexec.FakeAction{
   857  			// Test case 1: img1 and img2 exist, img3 doesn't exist
   858  			func() ([]byte, []byte, error) { return nil, nil, nil },
   859  			func() ([]byte, []byte, error) { return nil, nil, nil },
   860  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   861  
   862  			// Test case 2: images don't exist
   863  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   864  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   865  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   866  		},
   867  		CombinedOutputScript: []fakeexec.FakeAction{
   868  			// Test case1: pull only img3
   869  			func() ([]byte, []byte, error) { return []byte("pause"), nil, nil },
   870  			func() ([]byte, []byte, error) { return nil, nil, nil },
   871  			// Test case 2: fail to pull image2 and image3
   872  			// If the pull fails, it will be retried 5 times (see PullImageRetry in constants/constants.go)
   873  			func() ([]byte, []byte, error) { return []byte("pause"), nil, nil },
   874  			func() ([]byte, []byte, error) { return nil, nil, nil },
   875  			func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} },
   876  			func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} },
   877  			func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} },
   878  			func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} },
   879  			func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} },
   880  			func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} },
   881  			func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} },
   882  			func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} },
   883  			func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} },
   884  			func() ([]byte, []byte, error) { return []byte("error"), nil, &fakeexec.FakeExitError{Status: 1} },
   885  		},
   886  	}
   887  
   888  	fexec := &fakeexec.FakeExec{
   889  		CommandScript: []fakeexec.FakeCommandAction{
   890  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   891  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   892  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   893  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   894  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   895  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   896  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   897  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   898  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   899  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   900  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   901  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   902  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   903  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   904  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   905  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   906  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   907  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   908  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   909  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   910  		},
   911  		LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
   912  	}
   913  
   914  	containerRuntime, err := utilruntime.NewContainerRuntime(fexec, constants.DefaultCRISocket)
   915  	if err != nil {
   916  		t.Errorf("unexpected NewContainerRuntime error: %v", err)
   917  	}
   918  
   919  	check := ImagePullCheck{
   920  		runtime:         containerRuntime,
   921  		sandboxImage:    "pause",
   922  		imageList:       []string{"img1", "img2", "img3"},
   923  		imagePullPolicy: corev1.PullIfNotPresent,
   924  		imagePullSerial: true,
   925  	}
   926  	warnings, errors := check.Check()
   927  	if len(warnings) != 0 {
   928  		t.Fatalf("did not expect any warnings but got %q", warnings)
   929  	}
   930  	if len(errors) != 0 {
   931  		t.Fatalf("expected 1 errors but got %d: %q", len(errors), errors)
   932  	}
   933  
   934  	warnings, errors = check.Check()
   935  	if len(warnings) != 0 {
   936  		t.Fatalf("did not expect any warnings but got %q", warnings)
   937  	}
   938  	if len(errors) != 2 {
   939  		t.Fatalf("expected 2 errors but got %d: %q", len(errors), errors)
   940  	}
   941  
   942  	// Test with unknown policy
   943  	check = ImagePullCheck{
   944  		runtime:         containerRuntime,
   945  		sandboxImage:    "pause",
   946  		imageList:       []string{"img1", "img2", "img3"},
   947  		imagePullPolicy: "",
   948  		imagePullSerial: true,
   949  	}
   950  	_, errors = check.Check()
   951  	if len(errors) != 1 {
   952  		t.Fatalf("expected 1 error but got %d: %q", len(errors), errors)
   953  	}
   954  }
   955  
   956  func TestNumCPUCheck(t *testing.T) {
   957  	var tests = []struct {
   958  		numCPU      int
   959  		numErrors   int
   960  		numWarnings int
   961  	}{
   962  		{0, 0, 0},
   963  		{999999999, 1, 0},
   964  	}
   965  
   966  	for _, rt := range tests {
   967  		t.Run(fmt.Sprintf("number of CPUs: %d", rt.numCPU), func(t *testing.T) {
   968  			warnings, errors := NumCPUCheck{NumCPU: rt.numCPU}.Check()
   969  			if len(warnings) != rt.numWarnings {
   970  				t.Errorf("expected %d warning(s) but got %d: %q", rt.numWarnings, len(warnings), warnings)
   971  			}
   972  			if len(errors) != rt.numErrors {
   973  				t.Errorf("expected %d warning(s) but got %d: %q", rt.numErrors, len(errors), errors)
   974  			}
   975  		})
   976  	}
   977  }
   978  
   979  func TestMemCheck(t *testing.T) {
   980  	// skip this test, if OS in not Linux, since it will ONLY pass on Linux.
   981  	if runtime.GOOS != "linux" {
   982  		t.Skip("unsupported OS for memory check test ")
   983  	}
   984  
   985  	var tests = []struct {
   986  		minimum        uint64
   987  		expectedErrors int
   988  	}{
   989  		{0, 0},
   990  		{9999999999999999, 1},
   991  	}
   992  
   993  	for _, rt := range tests {
   994  		t.Run(fmt.Sprintf("MemoryCheck{%d}", rt.minimum), func(t *testing.T) {
   995  			warnings, errors := MemCheck{Mem: rt.minimum}.Check()
   996  			if len(warnings) > 0 {
   997  				t.Errorf("expected 0 warnings but got %d: %q", len(warnings), warnings)
   998  			} else if len(errors) != rt.expectedErrors {
   999  				t.Errorf("expected %d error(s) but got %d: %q", rt.expectedErrors, len(errors), errors)
  1000  			}
  1001  		})
  1002  	}
  1003  }
  1004  
  1005  func TestInitIPCheck(t *testing.T) {
  1006  	// skip this test, if OS in not Linux, since it will ONLY pass on Linux.
  1007  	if runtime.GOOS != "linux" {
  1008  		t.Skip("unsupported OS")
  1009  	}
  1010  	// should be a privileged user for the `init` command, otherwise just skip it.
  1011  	isPrivileged := IsPrivilegedUserCheck{}
  1012  	if _, err := isPrivileged.Check(); err != nil {
  1013  		t.Skip("not a privileged user")
  1014  	}
  1015  	internalcfg, err := configutil.DefaultedStaticInitConfiguration()
  1016  	if err != nil {
  1017  		t.Fatalf("unexpected failure when defaulting InitConfiguration: %v", err)
  1018  	}
  1019  	internalcfg.LocalAPIEndpoint.AdvertiseAddress = "" // AdvertiseAddress is optional, it could be auto-detected.
  1020  	ipv4File := "FileContent--proc-sys-net-ipv4-ip_forward"
  1021  	ipv6File := "FileContent--proc-sys-net-ipv6-conf-default-forwarding"
  1022  	var tests = []struct {
  1023  		testName    string
  1024  		PodSubnet   string
  1025  		serviceCidr string
  1026  		expStr      []string
  1027  	}{
  1028  		{
  1029  			testName:    "dual stack",
  1030  			PodSubnet:   "fda9:d324:354d:0::/56",
  1031  			serviceCidr: "10.96.0.0/16,fda9:d324:354d:1::/112",
  1032  			expStr:      []string{"FileContent--proc-sys-net-ipv4-ip_forward", "FileContent--proc-sys-net-ipv6-conf-default-forwarding"},
  1033  		},
  1034  		{
  1035  			testName:    "single stack ipv4",
  1036  			PodSubnet:   "10.244.0.0/16",
  1037  			serviceCidr: "10.96.0.0/16",
  1038  			expStr:      []string{"FileContent--proc-sys-net-ipv4-ip_forward"},
  1039  		},
  1040  		{
  1041  			testName:    "single stack ipv6",
  1042  			PodSubnet:   "fda9:d324:354d:0::/56",
  1043  			serviceCidr: "fda9:d324:354d:1::/112",
  1044  			expStr:      []string{"FileContent--proc-sys-net-ipv6-conf-default-forwarding"},
  1045  		},
  1046  	}
  1047  
  1048  	for _, rt := range tests {
  1049  		t.Run(rt.testName, func(t *testing.T) {
  1050  			checkList := []string{}
  1051  			internalcfg.Networking.ServiceSubnet = rt.serviceCidr
  1052  			internalcfg.Networking.PodSubnet = rt.PodSubnet
  1053  			checks, err := InitNodeChecks(exec.New(), internalcfg, nil, false, false)
  1054  			if err != nil {
  1055  				t.Fatalf("unexpected error: %v", err)
  1056  			}
  1057  			for _, check := range checks {
  1058  				if check.Name() == ipv4File {
  1059  					checkList = append(checkList, ipv4File)
  1060  				}
  1061  				if check.Name() == ipv6File {
  1062  					checkList = append(checkList, ipv6File)
  1063  				}
  1064  			}
  1065  			if diff := cmp.Diff(checkList, rt.expStr); diff != "" {
  1066  				t.Fatalf("unexpected file content check (-want,+got):\n%s", diff)
  1067  			}
  1068  		})
  1069  	}
  1070  }
  1071  
  1072  func TestJoinIPCheck(t *testing.T) {
  1073  	// skip this test, if OS in not Linux, since it will ONLY pass on Linux.
  1074  	if runtime.GOOS != "linux" {
  1075  		t.Skip("unsupported OS")
  1076  	}
  1077  	// should be a privileged user for the `join` command, otherwise just skip it.
  1078  	isPrivileged := IsPrivilegedUserCheck{}
  1079  	if _, err := isPrivileged.Check(); err != nil {
  1080  		t.Skip("not a privileged user")
  1081  	}
  1082  
  1083  	opts := configutil.LoadOrDefaultConfigurationOptions{
  1084  		SkipCRIDetect: true,
  1085  	}
  1086  
  1087  	internalcfg, err := configutil.DefaultedJoinConfiguration(&kubeadmapiv1.JoinConfiguration{
  1088  		Discovery: kubeadmapiv1.Discovery{
  1089  			BootstrapToken: &kubeadmapiv1.BootstrapTokenDiscovery{
  1090  				Token:                    configutil.PlaceholderToken.Token.String(),
  1091  				APIServerEndpoint:        "kube-apiserver:6443",
  1092  				UnsafeSkipCAVerification: true,
  1093  			},
  1094  		},
  1095  	}, opts)
  1096  	if err != nil {
  1097  		t.Fatalf("unexpected failure when defaulting JoinConfiguration: %v", err)
  1098  	}
  1099  	ipv4File := "FileContent--proc-sys-net-ipv4-ip_forward"
  1100  	ipv6File := "FileContent--proc-sys-net-ipv6-conf-default-forwarding"
  1101  	var tests = []struct {
  1102  		testName string
  1103  		endpoint string
  1104  		expStr   []string
  1105  	}{
  1106  		{
  1107  			testName: "single stack ipv4",
  1108  			endpoint: "10.244.0.0:1234",
  1109  			expStr:   []string{"FileContent--proc-sys-net-ipv4-ip_forward"},
  1110  		},
  1111  		{
  1112  			testName: "single stack ipv6",
  1113  			endpoint: "[fda9:d324:354d:0::]:1234",
  1114  			expStr:   []string{"FileContent--proc-sys-net-ipv6-conf-default-forwarding"},
  1115  		},
  1116  	}
  1117  
  1118  	for _, rt := range tests {
  1119  		t.Run(rt.testName, func(t *testing.T) {
  1120  			checkList := []string{}
  1121  			internalcfg.Discovery.BootstrapToken.APIServerEndpoint = rt.endpoint
  1122  			checks, err := JoinNodeChecks(exec.New(), internalcfg, nil)
  1123  			if err != nil {
  1124  				t.Fatalf("unexpected error: %v", err)
  1125  			}
  1126  			for _, check := range checks {
  1127  				if check.Name() == ipv4File {
  1128  					checkList = append(checkList, ipv4File)
  1129  				}
  1130  				if check.Name() == ipv6File {
  1131  					checkList = append(checkList, ipv6File)
  1132  				}
  1133  			}
  1134  			if diff := cmp.Diff(checkList, rt.expStr); diff != "" {
  1135  				t.Fatalf("unexpected file content check (-want,+got):\n%s", diff)
  1136  			}
  1137  		})
  1138  	}
  1139  }
  1140  

View as plain text