github.com/jenkins-x/test-infra@v0.0.7/scenarios/kubernetes_e2e_test.py (about)

     1  #!/usr/bin/env python
     2  
     3  # Copyright 2017 The Kubernetes Authors.
     4  #
     5  # Licensed under the Apache License, Version 2.0 (the "License");
     6  # you may not use this file except in compliance with the License.
     7  # You may obtain a copy of the License at
     8  #
     9  #     http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  
    17  # Need to figure out why this only fails on travis
    18  # pylint: disable=too-few-public-methods
    19  
    20  """Test for kubernetes_e2e.py"""
    21  
    22  import os
    23  import shutil
    24  import string
    25  import tempfile
    26  import urllib2
    27  import unittest
    28  import time
    29  
    30  import kubernetes_e2e
    31  
    32  FAKE_WORKSPACE_STATUS = 'STABLE_BUILD_GIT_COMMIT 599539dc0b99976fda0f326f4ce47e93ec07217c\n' \
    33  'STABLE_BUILD_SCM_STATUS clean\n' \
    34  'STABLE_BUILD_SCM_REVISION v1.7.0-alpha.0.1320+599539dc0b9997\n' \
    35  'STABLE_BUILD_MAJOR_VERSION 1\n' \
    36  'STABLE_BUILD_MINOR_VERSION 7+\n' \
    37  'STABLE_gitCommit 599539dc0b99976fda0f326f4ce47e93ec07217c\n' \
    38  'STABLE_gitTreeState clean\n' \
    39  'STABLE_gitVersion v1.7.0-alpha.0.1320+599539dc0b9997\n' \
    40  'STABLE_gitMajor 1\n' \
    41  'STABLE_gitMinor 7+\n'
    42  
    43  FAKE_WORKSPACE_STATUS_V1_6 = 'STABLE_BUILD_GIT_COMMIT 84febd4537dd190518657405b7bdb921dfbe0387\n' \
    44  'STABLE_BUILD_SCM_STATUS clean\n' \
    45  'STABLE_BUILD_SCM_REVISION v1.6.4-beta.0.18+84febd4537dd19\n' \
    46  'STABLE_BUILD_MAJOR_VERSION 1\n' \
    47  'STABLE_BUILD_MINOR_VERSION 6+\n' \
    48  'STABLE_gitCommit 84febd4537dd190518657405b7bdb921dfbe0387\n' \
    49  'STABLE_gitTreeState clean\n' \
    50  'STABLE_gitVersion v1.6.4-beta.0.18+84febd4537dd19\n' \
    51  'STABLE_gitMajor 1\n' \
    52  'STABLE_gitMinor 6+\n'
    53  
    54  FAKE_DESCRIBE_FROM_FAMILY_RESPONSE = """
    55  archiveSizeBytes: '1581831882'
    56  creationTimestamp: '2017-06-16T10:37:57.681-07:00'
    57  description: 'Google, Container-Optimized OS, 59-9460.64.0 stable, Kernel: ChromiumOS-4.4.52
    58    Kubernetes: 1.6.4 Docker: 1.11.2'
    59  diskSizeGb: '10'
    60  family: cos-stable
    61  id: '2388425242502080922'
    62  kind: compute#image
    63  labelFingerprint: 42WmSpB8rSM=
    64  licenses:
    65  - https://www.googleapis.com/compute/v1/projects/cos-cloud/global/licenses/cos
    66  name: cos-stable-59-9460-64-0
    67  rawDisk:
    68    containerType: TAR
    69    source: ''
    70  selfLink: https://www.googleapis.com/compute/v1/projects/cos-cloud/global/images/cos-stable-59-9460-64-0
    71  sourceType: RAW
    72  status: READY
    73  """
    74  
    75  def fake_pass(*_unused, **_unused2):
    76      """Do nothing."""
    77      pass
    78  
    79  def fake_bomb(*a, **kw):
    80      """Always raise."""
    81      raise AssertionError('Should not happen', a, kw)
    82  
    83  def raise_urllib2_error(*_unused, **_unused2):
    84      """Always raise a urllib2.URLError"""
    85      raise urllib2.URLError("test failure")
    86  
    87  def always_kubernetes(*_unused, **_unused2):
    88      """Always return 'kubernetes'"""
    89      return 'kubernetes'
    90  
    91  class Stub(object):
    92      """Replace thing.param with replacement until exiting with."""
    93      def __init__(self, thing, param, replacement):
    94          self.thing = thing
    95          self.param = param
    96          self.replacement = replacement
    97          self.old = getattr(thing, param)
    98          setattr(thing, param, self.replacement)
    99  
   100      def __enter__(self, *a, **kw):
   101          return self.replacement
   102  
   103      def __exit__(self, *a, **kw):
   104          setattr(self.thing, self.param, self.old)
   105  
   106  
   107  class ClusterNameTest(unittest.TestCase):
   108      def test_name_filled(self):
   109          """Return the cluster name if set."""
   110          name = 'foo'
   111          build = '1984'
   112          os.environ['BUILD_ID'] = build
   113          actual = kubernetes_e2e.cluster_name(name)
   114          self.assertTrue(actual)
   115          self.assertIn(name, actual)
   116          self.assertNotIn(build, actual)
   117  
   118      def test_name_empty_short_build(self):
   119          """Return the build number if name is empty."""
   120          name = ''
   121          build = '1984'
   122          os.environ['BUILD_ID'] = build
   123          actual = kubernetes_e2e.cluster_name(name)
   124          self.assertTrue(actual)
   125          self.assertIn(build, actual)
   126  
   127      def test_name_empty_long_build(self):
   128          """Return a short hash of a long build number if name is empty."""
   129          name = ''
   130          build = '0' * 63
   131          os.environ['BUILD_ID'] = build
   132          actual = kubernetes_e2e.cluster_name(name)
   133          self.assertTrue(actual)
   134          self.assertNotIn(build, actual)
   135          if len(actual) > 32:  # Some firewall names consume half the quota
   136              self.fail('Name should be short: %s' % actual)
   137  
   138      def test_name_presubmit(self):
   139          """Return the build number if name is empty."""
   140          name = ''
   141          build = '1984'
   142          pr = '12345'
   143          os.environ['BUILD_ID'] = build
   144          os.environ['JOB_TYPE'] = 'presubmit'
   145          os.environ['PULL_NUMBER'] = pr
   146          actual = kubernetes_e2e.cluster_name(name, False)
   147          self.assertTrue(actual)
   148          self.assertIn(build, actual)
   149          self.assertNotIn(pr, actual)
   150  
   151          actual = kubernetes_e2e.cluster_name(name, True)
   152          self.assertTrue(actual)
   153          self.assertIn(pr, actual)
   154          self.assertNotIn(build, actual)
   155  
   156  
   157  class ScenarioTest(unittest.TestCase):  # pylint: disable=too-many-public-methods
   158      """Test for e2e scenario."""
   159      callstack = []
   160      envs = {}
   161  
   162      def setUp(self):
   163          self.boiler = [
   164              Stub(kubernetes_e2e, 'check', self.fake_check),
   165              Stub(shutil, 'copy', fake_pass),
   166          ]
   167  
   168      def tearDown(self):
   169          for stub in self.boiler:
   170              with stub:  # Leaving with restores things
   171                  pass
   172          self.callstack[:] = []
   173          self.envs.clear()
   174  
   175      def fake_check(self, *cmd):
   176          """Log the command."""
   177          self.callstack.append(string.join(cmd))
   178  
   179      def fake_check_env(self, env, *cmd):
   180          """Log the command with a specific env."""
   181          self.envs.update(env)
   182          self.callstack.append(string.join(cmd))
   183  
   184      def fake_output_work_status(self, *cmd):
   185          """fake a workstatus blob."""
   186          self.callstack.append(string.join(cmd))
   187          return FAKE_WORKSPACE_STATUS
   188  
   189      def fake_output_work_status_v1_6(self, *cmd):
   190          """fake a workstatus blob for v1.6."""
   191          self.callstack.append(string.join(cmd))
   192          return FAKE_WORKSPACE_STATUS_V1_6
   193  
   194      def fake_output_get_latest_image(self, *cmd):
   195          """fake a `gcloud compute images describe-from-family` response."""
   196          self.callstack.append(string.join(cmd))
   197          return FAKE_DESCRIBE_FROM_FAMILY_RESPONSE
   198  
   199      def test_local(self):
   200          """Make sure local mode is fine overall."""
   201          args = kubernetes_e2e.parse_args()
   202          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   203              kubernetes_e2e.main(args)
   204  
   205          self.assertNotEqual(self.envs, {})
   206          for call in self.callstack:
   207              self.assertFalse(call.startswith('docker'))
   208  
   209      def test_check_leaks(self):
   210          """Ensure --check-leaked-resources=true sends flag to kubetest."""
   211          args = kubernetes_e2e.parse_args(['--check-leaked-resources=true'])
   212          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   213              kubernetes_e2e.main(args)
   214              self.assertIn('--check-leaked-resources=true', self.callstack[-1])
   215  
   216      def test_check_leaks_false(self):
   217          """Ensure --check-leaked-resources=true sends flag to kubetest."""
   218          args = kubernetes_e2e.parse_args(['--check-leaked-resources=false'])
   219          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   220              kubernetes_e2e.main(args)
   221              self.assertIn('--check-leaked-resources=false', self.callstack[-1])
   222  
   223      def test_check_leaks_default(self):
   224          """Ensure --check-leaked-resources=true sends flag to kubetest."""
   225          args = kubernetes_e2e.parse_args(['--check-leaked-resources'])
   226          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   227              kubernetes_e2e.main(args)
   228              self.assertIn('--check-leaked-resources', self.callstack[-1])
   229  
   230      def test_check_leaks_unset(self):
   231          """Ensure --check-leaked-resources=true sends flag to kubetest."""
   232          args = kubernetes_e2e.parse_args(['--mode=local'])
   233          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   234              kubernetes_e2e.main(args)
   235              self.assertNotIn('--check-leaked-resources', self.callstack[-1])
   236  
   237      def test_migrated_kubetest_args(self):
   238          migrated = [
   239              '--stage-suffix=panda',
   240              '--random-flag', 'random-value',
   241              '--multiple-federations',
   242              'arg1', 'arg2',
   243              '--federation',
   244              '--kubemark',
   245              '--extract=this',
   246              '--extract=that',
   247              '--save=somewhere',
   248              '--skew',
   249              '--publish=location',
   250              '--timeout=42m',
   251              '--upgrade_args=ginkgo',
   252              '--check-leaked-resources=true',
   253              '--charts',
   254          ]
   255          explicit_passthrough_args = [
   256              '--deployment=yay',
   257              '--provider=gce',
   258          ]
   259          args = kubernetes_e2e.parse_args(migrated
   260                                           + explicit_passthrough_args
   261                                           + ['--test=false'])
   262          self.assertEquals(migrated, args.kubetest_args)
   263          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   264              kubernetes_e2e.main(args)
   265          lastcall = self.callstack[-1]
   266          for arg in migrated:
   267              self.assertIn(arg, lastcall)
   268          for arg in explicit_passthrough_args:
   269              self.assertIn(arg, lastcall)
   270  
   271      def test_updown_default(self):
   272          args = kubernetes_e2e.parse_args([])
   273          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   274              kubernetes_e2e.main(args)
   275          lastcall = self.callstack[-1]
   276          self.assertIn('--up', lastcall)
   277          self.assertIn('--down', lastcall)
   278  
   279      def test_updown_set(self):
   280          args = kubernetes_e2e.parse_args(['--up=false', '--down=true'])
   281          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   282              kubernetes_e2e.main(args)
   283          lastcall = self.callstack[-1]
   284          self.assertNotIn('--up', lastcall)
   285          self.assertIn('--down', lastcall)
   286  
   287  
   288      def test_kubeadm_ci(self):
   289          """Make sure kubeadm ci mode is fine overall."""
   290          args = kubernetes_e2e.parse_args(['--kubeadm=ci'])
   291          self.assertEqual(args.kubeadm, 'ci')
   292          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   293              with Stub(kubernetes_e2e, 'check_output', self.fake_output_work_status):
   294                  kubernetes_e2e.main(args)
   295  
   296          self.assertNotIn('E2E_OPT', self.envs)
   297          version = 'gs://kubernetes-release-dev/ci/v1.7.0-alpha.0.1320+599539dc0b9997-bazel/bin/linux/amd64/'  # pylint: disable=line-too-long
   298          self.assertIn('--kubernetes-anywhere-kubeadm-version=%s' % version, self.callstack[-1])
   299          called = False
   300          for call in self.callstack:
   301              self.assertFalse(call.startswith('docker'))
   302              if call == 'hack/print-workspace-status.sh':
   303                  called = True
   304          self.assertTrue(called)
   305  
   306      def test_local_env(self):
   307          """
   308              Ensure that host variables (such as GOPATH) are included,
   309              and added envs/env files overwrite os environment.
   310          """
   311          mode = kubernetes_e2e.LocalMode('/orig-workspace', '/random-artifacts')
   312          mode.add_environment(*(
   313              'FOO=BAR', 'GOPATH=/go/path', 'WORKSPACE=/new/workspace'))
   314          mode.add_os_environment(*('USER=jenkins', 'FOO=BAZ', 'GOOS=linux'))
   315          with tempfile.NamedTemporaryFile() as temp:
   316              temp.write('USER=prow')
   317              temp.flush()
   318              mode.add_file(temp.name)
   319          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   320              mode.start([])
   321          self.assertIn(('FOO', 'BAR'), self.envs.viewitems())
   322          self.assertIn(('WORKSPACE', '/new/workspace'), self.envs.viewitems())
   323          self.assertIn(('GOPATH', '/go/path'), self.envs.viewitems())
   324          self.assertIn(('USER', 'prow'), self.envs.viewitems())
   325          self.assertIn(('GOOS', 'linux'), self.envs.viewitems())
   326          self.assertNotIn(('USER', 'jenkins'), self.envs.viewitems())
   327          self.assertNotIn(('FOO', 'BAZ'), self.envs.viewitems())
   328  
   329      def test_kubeadm_periodic(self):
   330          """Make sure kubeadm periodic mode is fine overall."""
   331          args = kubernetes_e2e.parse_args(['--kubeadm=periodic'])
   332          self.assertEqual(args.kubeadm, 'periodic')
   333          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   334              with Stub(kubernetes_e2e, 'check_output', self.fake_output_work_status):
   335                  kubernetes_e2e.main(args)
   336  
   337          self.assertNotIn('E2E_OPT', self.envs)
   338          version = 'gs://kubernetes-release-dev/ci/v1.7.0-alpha.0.1320+599539dc0b9997-bazel/bin/linux/amd64/'  # pylint: disable=line-too-long
   339          self.assertIn('--kubernetes-anywhere-kubeadm-version=%s' % version, self.callstack[-1])
   340          called = False
   341          for call in self.callstack:
   342              self.assertFalse(call.startswith('docker'))
   343              if call == 'hack/print-workspace-status.sh':
   344                  called = True
   345          self.assertTrue(called)
   346  
   347      def test_kubeadm_pull(self):
   348          """Make sure kubeadm pull mode is fine overall."""
   349          args = kubernetes_e2e.parse_args([
   350              '--kubeadm=pull',
   351              '--use-shared-build=bazel'
   352          ])
   353          self.assertEqual(args.kubeadm, 'pull')
   354          self.assertEqual(args.use_shared_build, 'bazel')
   355  
   356          gcs_bucket = "gs://kubernetes-release-dev/bazel/v1.8.0-beta.1.132+599539dc0b9997"
   357  
   358          def fake_gcs_path(path):
   359              bazel_default = os.path.join(
   360                  'gs://kubernetes-jenkins/shared-results', 'bazel-build-location.txt')
   361              self.assertEqual(path, bazel_default)
   362              return gcs_bucket
   363          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   364              with Stub(kubernetes_e2e, 'read_gcs_path', fake_gcs_path):
   365                  kubernetes_e2e.main(args)
   366  
   367          self.assertNotIn('E2E_OPT', self.envs)
   368          version = '%s/bin/linux/amd64/' % gcs_bucket
   369          self.assertIn('--kubernetes-anywhere-kubeadm-version=%s' % version, self.callstack[-1])
   370  
   371      def test_kubeadm_invalid(self):
   372          """Make sure kubeadm invalid mode exits unsuccessfully."""
   373          with self.assertRaises(SystemExit) as sysexit:
   374              kubernetes_e2e.parse_args(['--mode=local', '--kubeadm=deploy'])
   375  
   376          self.assertEqual(sysexit.exception.code, 2)
   377  
   378      def test_parse_args_order_agnostic(self):
   379          args = kubernetes_e2e.parse_args([
   380              '--some-kubetest-arg=foo',
   381              '--cluster=test'])
   382          self.assertEqual(args.kubetest_args, ['--some-kubetest-arg=foo'])
   383          self.assertEqual(args.cluster, 'test')
   384  
   385      def test_gcp_network(self):
   386          args = kubernetes_e2e.parse_args(['--mode=local', '--cluster=test'])
   387          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   388              kubernetes_e2e.main(args)
   389          lastcall = self.callstack[-1]
   390          self.assertIn('--gcp-network=test', lastcall)
   391  
   392      def test_env_local(self):
   393          env = 'FOO'
   394          value = 'BLAT'
   395          args = kubernetes_e2e.parse_args([
   396              '--mode=local',
   397              '--env={env}={value}'.format(env=env, value=value),
   398          ])
   399          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   400              kubernetes_e2e.main(args)
   401          self.assertIn(env, self.envs)
   402          self.assertEqual(self.envs[env], value)
   403  
   404      def test_aws(self):
   405          temp = tempfile.NamedTemporaryFile()
   406          args = kubernetes_e2e.parse_args([
   407              '--aws',
   408              '--cluster=foo',
   409              '--aws-cluster-domain=test-aws.k8s.io',
   410              '--aws-ssh=%s' % temp.name,
   411              '--aws-pub=%s' % temp.name,
   412              '--aws-cred=%s' % temp.name,
   413              ])
   414          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   415              kubernetes_e2e.main(args)
   416  
   417          lastcall = self.callstack[-1]
   418          self.assertIn('kops-e2e-runner.sh', lastcall)
   419          self.assertIn('--kops-cluster=foo.test-aws.k8s.io', lastcall)
   420          self.assertIn('--kops-zones', lastcall)
   421          self.assertIn('--kops-state=s3://k8s-kops-prow/', lastcall)
   422          self.assertIn('--kops-nodes=4', lastcall)
   423          self.assertIn('--kops-ssh-key', lastcall)
   424  
   425          self.assertNotIn('kubetest', lastcall)
   426          self.assertIn('kops-e2e-runner.sh', lastcall)
   427  
   428          self.assertEqual(
   429              self.envs['JENKINS_AWS_SSH_PRIVATE_KEY_FILE'], temp.name)
   430          self.assertEqual(
   431              self.envs['JENKINS_AWS_SSH_PUBLIC_KEY_FILE'], temp.name)
   432          self.assertEqual(
   433              self.envs['JENKINS_AWS_CREDENTIALS_FILE'], temp.name)
   434  
   435      def test_kops_aws(self):
   436          temp = tempfile.NamedTemporaryFile()
   437          args = kubernetes_e2e.parse_args([
   438              '--provider=aws',
   439              '--deployment=kops',
   440              '--cluster=foo.example.com',
   441              '--aws-ssh=%s' % temp.name,
   442              '--aws-pub=%s' % temp.name,
   443              '--aws-cred=%s' % temp.name,
   444              ])
   445          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   446              kubernetes_e2e.main(args)
   447  
   448          lastcall = self.callstack[-1]
   449          self.assertIn('kubetest', lastcall)
   450          self.assertIn('--provider=aws', lastcall)
   451          self.assertIn('--deployment=kops', lastcall)
   452          self.assertIn('--kops-cluster=foo.example.com', lastcall)
   453          self.assertIn('--kops-zones', lastcall)
   454          self.assertIn('--kops-state=s3://k8s-kops-prow/', lastcall)
   455          self.assertIn('--kops-nodes=4', lastcall)
   456          self.assertIn('--kops-ssh-key', lastcall)
   457          self.assertIn('kubetest', lastcall)
   458          self.assertNotIn('kops-e2e-runner.sh', lastcall)
   459  
   460      def test_kops_gce(self):
   461          temp = tempfile.NamedTemporaryFile()
   462          args = kubernetes_e2e.parse_args([
   463              '--provider=gce',
   464              '--deployment=kops',
   465              '--cluster=foo.example.com',
   466              '--gce-ssh=%s' % temp.name,
   467              '--gce-pub=%s' % temp.name,
   468              ])
   469          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   470              kubernetes_e2e.main(args)
   471  
   472          lastcall = self.callstack[-1]
   473          self.assertIn('kubetest', lastcall)
   474          self.assertIn('--provider=gce', lastcall)
   475          self.assertIn('--deployment=kops', lastcall)
   476          self.assertIn('--kops-cluster=foo.example.com', lastcall)
   477          self.assertIn('--kops-zones', lastcall)
   478          self.assertIn('--kops-state=gs://k8s-kops-gce/', lastcall)
   479          self.assertIn('--kops-nodes=4', lastcall)
   480          self.assertIn('--kops-ssh-key', lastcall)
   481  
   482      def test_use_shared_build(self):
   483          # normal path
   484          args = kubernetes_e2e.parse_args([
   485              '--use-shared-build=bazel'
   486          ])
   487          def expect_bazel_gcs(path):
   488              bazel_default = os.path.join(
   489                  'gs://kubernetes-jenkins/shared-results', 'bazel-build-location.txt')
   490              self.assertEqual(path, bazel_default)
   491              return always_kubernetes()
   492          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   493              with Stub(kubernetes_e2e, 'read_gcs_path', expect_bazel_gcs):
   494                  with Stub(time, 'sleep', fake_pass):
   495                      kubernetes_e2e.main(args)
   496          lastcall = self.callstack[-1]
   497          self.assertIn('--extract=kubernetes', lastcall)
   498          # normal path, not bazel
   499          args = kubernetes_e2e.parse_args([
   500              '--use-shared-build'
   501          ])
   502          def expect_normal_gcs(path):
   503              bazel_default = os.path.join(
   504                  'gs://kubernetes-jenkins/shared-results', 'build-location.txt')
   505              self.assertEqual(path, bazel_default)
   506              return always_kubernetes()
   507          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   508              with Stub(kubernetes_e2e, 'read_gcs_path', expect_normal_gcs):
   509                  kubernetes_e2e.main(args)
   510          lastcall = self.callstack[-1]
   511          self.assertIn('--extract=kubernetes', lastcall)
   512          # test failure to read shared path from GCS
   513          with Stub(kubernetes_e2e, 'check_env', self.fake_check_env):
   514              with Stub(kubernetes_e2e, 'read_gcs_path', raise_urllib2_error):
   515                  with Stub(os, 'getcwd', always_kubernetes):
   516                      with Stub(time, 'sleep', fake_pass):
   517                          try:
   518                              kubernetes_e2e.main(args)
   519                          except RuntimeError as err:
   520                              if not err.message.startswith('Failed to get shared build location'):
   521                                  raise err
   522  
   523  if __name__ == '__main__':
   524      unittest.main()