diff --git a/.github/workflows/test-driver.yml b/.github/workflows/test-driver.yml index 0611bec..ad8a30b 100644 --- a/.github/workflows/test-driver.yml +++ b/.github/workflows/test-driver.yml @@ -12,8 +12,18 @@ jobs: runs-on: windows-2016 steps: - uses: actions/checkout@v2 - - name: Install Prerequisites (PFX, IIS) - run: Import-PfxCertificate -FilePath .\test\test.pfx -CertStoreLocation Cert:\\LocalMachine\\My -Password (ConvertTo-SecureString -String 'Test123!' -AsPlainText -Force) + - name: Build Driver + run: go build -o win_iis.exe . + - name: Install Prerequisites shell: powershell + run: .\scripts\win_provision.ps1 + # - name: Install Nomad + # run: choco install nomad 1.0.4 + # - name: + # - name: Install Prerequisites (PFX, IIS) + # run: Import-PfxCertificate -FilePath .\test\test.pfx -CertStoreLocation Cert:\\LocalMachine\\My -Password (ConvertTo-SecureString -String 'Test123!' -AsPlainText -Force) + # shell: powershell - name: Run iis-driver integration tests run: go test ./iis/ -count=1 -v + - name: Run iis-driver end to end tests + run: go test ./test/e2e -count=1 -v diff --git a/Makefile b/Makefile index 965d50c..c00cf23 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ endif default: build -.PHONY: clean +.PHONY: clean test clean: ${RMCMD} ${PLUGIN_BINARY} vagrant destroy -f @@ -24,5 +24,7 @@ up: converge: build up vagrant provision -test: converge +test: converge vagrant winrm -s cmd -c 'chdir C:\vagrant && go test ./iis/ -count=1 -v' + vagrant winrm -s cmd -c 'chdir C:\vagrant && go test ./test/e2e -count=1 -v' + diff --git a/examples/iis-test.nomad b/examples/iis-test.nomad index 6fe6664..a5e6cae 100644 --- a/examples/iis-test.nomad +++ b/examples/iis-test.nomad @@ -7,13 +7,11 @@ job "iis-test" { network { port "httplabel" {} + port "httpslabel" {} } restart { - attempts = 10 - interval = "5m" - delay = "25s" - mode = "delay" + attempts = 0 } task "iis-test" { @@ -25,6 +23,12 @@ job "iis-test" { type = "http" port = "httplabel" } + + bindings { + type = "https" + port = "httpslabel" + cert_name = "WMSVC-SHA2" + } } template { diff --git a/go.mod b/go.mod index 18405f5..e8dfd69 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,6 @@ require ( github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 github.com/hashicorp/go-hclog v0.15.0 github.com/hashicorp/nomad v1.0.4 + github.com/hashicorp/nomad/api v0.0.0-20200529203653-c4416b26d3eb github.com/stretchr/testify v1.6.1 ) diff --git a/go.sum b/go.sum index f16984a..b299e7f 100644 --- a/go.sum +++ b/go.sum @@ -148,6 +148,7 @@ github.com/docker/cli v0.0.0-20200303215952-eb310fca4956/go.mod h1:JLrzqnKDaYBop github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v17.12.0-ce-rc1.0.20200330121334-7f8b4b621b5d+incompatible h1:x+LFTD06kE2vF3WSFCG3D4XdqE2FD61524u/cXQLgws= github.com/docker/docker v17.12.0-ce-rc1.0.20200330121334-7f8b4b621b5d+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.2-0.20180719074751-73e5f5dbfea3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= @@ -155,6 +156,7 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libnetwork v0.8.0-dev.2.0.20200612180813-9e99af28df21/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= @@ -165,7 +167,6 @@ github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0/go.mod github.com/elazarl/go-bindata-assetfs v1.0.1-0.20200509193318-234c15e7648f/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/endocrimes/go-winio v0.4.13-0.20190628114223-fb47a8b41948/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s= @@ -175,8 +176,10 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/frankban/quicktest v1.4.0 h1:rCSCih1FnSWJEel/eub9wclBSqpF2F/PuvxUWGWnbO8= github.com/frankban/quicktest v1.4.0/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.6.5/go.mod h1:GOdftxWLWIbIWKbIMDroKFJzPdg6Iw7r+jX1DDZdVsA= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= @@ -209,6 +212,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -228,12 +232,15 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= @@ -257,6 +264,7 @@ github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.2.1-0.20200228141219-3ce3d519df39 h1:MqvH60+R2JhSdvVgGxmExOndrkRQtGW7w4+gcrymN64= @@ -268,6 +276,7 @@ github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT github.com/hashicorp/consul/api v1.8.1 h1:BOEQaMWoGMhmQ29fC26bi0qb7/rId9JzZP2V0Xmx7m8= github.com/hashicorp/consul/api v1.8.1/go.mod h1:sDjTOq0yUyv5G4h+BqSea7Fn6BU+XbolEz1952UB+mk= github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= +github.com/hashicorp/consul/sdk v0.7.0 h1:H6R9d008jDcHPQPAqPNuydAshJ4v5/8URdFnUvK/+sc= github.com/hashicorp/consul/sdk v0.7.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= github.com/hashicorp/cronexpr v1.1.0/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c= @@ -301,8 +310,6 @@ github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrh github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-memdb v1.0.3/go.mod h1:LWQ8R70vPrS4OEY9k28D2z8/Zzyu34NVzeRibGAzHO0= github.com/hashicorp/go-memdb v1.3.0/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g= -github.com/hashicorp/go-msgpack v0.0.0-20191101193846-96ddbed8d05b h1:lB+3FXrgs94Mz066O5Yz59m3l/O0uEsf2jPiZyUpKTU= -github.com/hashicorp/go-msgpack v0.0.0-20191101193846-96ddbed8d05b/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs= @@ -316,6 +323,7 @@ github.com/hashicorp/go-plugin v1.0.2-0.20191004171845-809113480b55 h1:XzRWU4VSJ github.com/hashicorp/go-plugin v1.0.2-0.20191004171845-809113480b55/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-raftchunking v0.6.1/go.mod h1:cGlg3JtDy7qy6c/3Bu660Mic1JF+7lWqIwCFSb08fX0= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= @@ -326,6 +334,7 @@ github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0S github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.1-0.20191009193637-2046c9d0f0b0 h1:8gXJgyR86/nGv7IqrnmnKJVNxC9APdvAVSxc9mGxfKk= @@ -336,8 +345,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/gopsutil v2.17.13-0.20190117153606-62d5761ddb7d+incompatible h1:Om/wDjV3ffJ9FzakiekRC6AGJJXlauCkns8ZKNuRcJc= -github.com/hashicorp/gopsutil v2.17.13-0.20190117153606-62d5761ddb7d+incompatible/go.mod h1:Mz8xr0ujmCW1qsmWMIUDOc+RgaN9QMvrgnBzQF12nYM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-0.20201016140508-a07e7d50bbee h1:8B4HqvMUtYSjsGkYjiQGStc9pXffY2J+Z2SPQAj+wMY= github.com/hashicorp/hcl v1.0.1-0.20201016140508-a07e7d50bbee/go.mod h1:gwlu9+/P9MmKtYrMsHeFRZPXj2CTPm11TDnMeaRHS7g= @@ -348,10 +355,12 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q= github.com/hashicorp/nomad v1.0.4 h1:ZNP6kACeDctOL/vs3cTk0orYZPQa2ltC7jXxVYqLUbk= github.com/hashicorp/nomad v1.0.4/go.mod h1:JZx+1E1/ajixkE7tyJqCnmw6uN1fqpCKv8d9M42u7zg= +github.com/hashicorp/nomad/api v0.0.0-20200529203653-c4416b26d3eb h1:gFssj9eV5on4ZYpwTQl+LTrkebu+qCxuKpISPcMCH88= github.com/hashicorp/nomad/api v0.0.0-20200529203653-c4416b26d3eb/go.mod h1:DCi2k47yuUDzf2qWAK8E1RVmWgz/lc0jZQeEnICTxmY= github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= @@ -364,8 +373,10 @@ github.com/hashicorp/serf v0.9.3/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKEN github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= +github.com/hashicorp/vault/api v1.0.5-0.20190730042357-746c0b111519 h1:2qdbEUXjHohC+OYHtVU5lujvPAHPKYR4IMs9rsiUTk8= github.com/hashicorp/vault/api v1.0.5-0.20190730042357-746c0b111519/go.mod h1:i9PKqwFko/s/aihU1uuHGh/FaQS+Xcgvd9dvnfAvQb0= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= +github.com/hashicorp/vault/sdk v0.1.14-0.20190730042320-0dc007d98cc8 h1:fLUoZ8cI/pqlVCk09r88cVoY7ggKEl1A4e6Mujr3RvU= github.com/hashicorp/vault/sdk v0.1.14-0.20190730042320-0dc007d98cc8/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= @@ -400,13 +411,16 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/likexian/gokit v0.0.0-20190309162924-0a377eecf7aa/go.mod h1:QdfYv6y6qPA9pbBA2qXtoT8BMKha6UyNbxWGWl/9Jfk= @@ -435,6 +449,7 @@ github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vq github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= @@ -442,6 +457,7 @@ github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286/go.mod h1:l0 github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -476,6 +492,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.0.1-0.20180308005104-6934b124db28 h1:R9vmquWCeGmxTHUVnTQJrU4oPlgEn9+x48nwXSqkIKg= @@ -503,13 +520,16 @@ github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqi github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.2.5+incompatible h1:xOYu2+sKj87pJz7V+I7260354UlcRyAZUGhMCToTzVw= github.com/pierrec/lz4 v2.2.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -546,6 +566,7 @@ github.com/ryanuber/columnize v2.1.1-0.20170703205827-abc90934186a+incompatible/ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/seccomp/libseccomp-golang v0.9.2-0.20200314001724-bdab42bd5128/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= @@ -561,6 +582,7 @@ github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjM github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -639,8 +661,10 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -650,6 +674,7 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= @@ -691,6 +716,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -743,6 +769,7 @@ golang.org/x/text v0.3.3-0.20200306154105-06d492aade88/go.mod h1:5Zoc/QRtKVWzQhO golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -772,6 +799,7 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20180829000535-087779f1d2c9/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -783,6 +811,7 @@ google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -822,6 +851,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -831,6 +861,7 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -843,7 +874,9 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/iis/driver.go b/iis/driver.go index 9fb769c..2c00997 100644 --- a/iis/driver.go +++ b/iis/driver.go @@ -20,12 +20,15 @@ package iis import ( "context" "fmt" + "path/filepath" + "strings" "time" "github.com/hashicorp/go-hclog" log "github.com/hashicorp/go-hclog" "github.com/hashicorp/nomad/client/stats" "github.com/hashicorp/nomad/drivers/shared/eventer" + shelpers "github.com/hashicorp/nomad/helper/stats" "github.com/hashicorp/nomad/plugins/base" "github.com/hashicorp/nomad/plugins/drivers" "github.com/hashicorp/nomad/plugins/shared/hclspec" @@ -40,7 +43,7 @@ const ( // pluginVersion allows the client to identify and use newer versions of // an installed plugin - pluginVersion = "0.2.0" + pluginVersion = "0.2.2" // fingerprintPeriod is the interval at which the plugin will send // fingerprint responses @@ -88,6 +91,7 @@ var ( "ipaddress": hclspec.NewAttr("ipaddress", "string", false), "port": hclspec.NewAttr("port", "string", true), "type": hclspec.NewAttr("type", "string", true), + "cert_name": hclspec.NewAttr("cert_name", "string", false), "cert_hash": hclspec.NewAttr("cert_hash", "string", false), })), }) @@ -252,7 +256,7 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint { } // Check if IIS is running in SC - if isRunning, err := isIISRunning(); err != nil { + if isRunning, err := IsIISRunning(); err != nil { d.logger.Error("Error in building fingerprint, when trying to get IIS running status: %v", err) fp.Health = drivers.HealthStateUndetected fp.HealthDescription = "Undetected" @@ -300,18 +304,155 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive userCpuStats: stats.NewCpuStats(), systemCpuStats: stats.NewCpuStats(), websiteStarted: false, + waitCh: make(chan struct{}), } driverState := TaskState{ StartedAt: h.startedAt, } + // Every executor runs this init at creation for stats + if err := shelpers.Init(); err != nil { + h.logger.Error("unable to initialize stats", "error", err) + } + + websiteConfig := WebsiteConfig{ + Name: h.taskConfig.AllocID, + Env: map[string]string{}, + AppPoolIdentity: iisAppPoolIdentity{ + Identity: driverConfig.AppPoolIdentity, + }, + AppPoolConfigPath: driverConfig.AppPoolConfigPath, + SiteConfigPath: driverConfig.SiteConfigPath, + } + + // Setup environment variables. + // NOMAD_APPPOOL_* are keywords for applying user/pass info for a given Application Pool in a secure manner + for key, val := range h.taskConfig.Env { + switch key { + case "NOMAD_APPPOOL_USERNAME": + websiteConfig.AppPoolIdentity.Identity = "SpecificUser" + websiteConfig.AppPoolIdentity.Username = val + case "NOMAD_APPPOOL_PASSWORD": + websiteConfig.AppPoolIdentity.Password = val + default: + websiteConfig.Env[key] = val + } + } + + if !filepath.IsAbs(driverConfig.Path) { + websiteConfig.Path = filepath.Join(h.taskConfig.TaskDir().Dir, driverConfig.Path) + } else { + websiteConfig.Path = driverConfig.Path + } + + var iisBindings []iisBinding + // If any bindings were specified, we move forward with port label cross lookups + if len(driverConfig.Bindings) > 0 { + if h.taskConfig.Resources.Ports != nil { + // parse group/shared resource ports. This is the preferred route for establishing network ports + // here is the relevant PR for the docker driver that drove this change: https://github.com/hashicorp/nomad/pull/8623 + + for _, binding := range driverConfig.Bindings { + if port, ok := h.taskConfig.Resources.Ports.Get(binding.PortLabel); ok { + binding.Port = port.Value + iisBindings = append(iisBindings, binding) + } else { + // errMsg := fmt.Sprintf("Port %s not found, check network stanza", binding.PortLabel) + // h.handleError(errMsg, errors.New(errMsg)) + // return + return nil, nil, fmt.Errorf("Port %s not found, check network stanza", binding.PortLabel) + } + } + } else if len(h.taskConfig.Resources.NomadResources.Networks) > 0 { + // parses a task's network stanza for dynamic/static ports + // this is deprecated as of Nomad v1.0+, in time this should be removed + // just like the docker driver, you can only work with one network stanza format over another + + for _, binding := range driverConfig.Bindings { + foundPort := false + for _, network := range h.taskConfig.Resources.NomadResources.Networks { + + for _, port := range network.ReservedPorts { + binding.Port = port.Value + iisBindings = append(iisBindings, binding) + foundPort = true + } + + for _, port := range network.DynamicPorts { + binding.Port = port.Value + iisBindings = append(iisBindings, binding) + foundPort = true + } + } + if !foundPort { + //errMsg := fmt.Sprintf("Port %s not found, check network stanza", binding.PortLabel) + //h.handleError(errMsg, errors.New(errMsg)) + return nil, nil, fmt.Errorf("Port %s not found, check network stanza", binding.PortLabel) + } + } + } + } + + // Validate config bindings for https + // First we gather currently installed certs + // This may be best lived in iis.go with the bindings code + // For now, it is here as I hack through tests and migrating code to other PRs + certs, err := getIISCerts() + if err != nil { + return nil, nil, fmt.Errorf("Failed to gather installed certs: %v", err) + } + for i := 0; i < len(iisBindings); i++ { + if iisBindings[i].CertHash != "" { + // check if cert thumbprint(hash) exists + certExists := false + for _, cert := range certs { + if strings.EqualFold(cert.Thumbprint, iisBindings[i].CertHash) { + certExists = true + break + } + } + if !certExists { + return nil, nil, fmt.Errorf("Failed to find cert_hash with thumbprint of '%s'", iisBindings[i].CertHash) + } + } else if iisBindings[i].CertName != "" { + certExists := false + var maxExpirationDate time.Time + for _, cert := range certs { + // Find certs with the same FriendlyName or CN + // If there exists a cert with the names we are looking for, then ensure we get the one with the further expiration date + if cert.FriendlyName == iisBindings[i].CertName || cert.CN == iisBindings[i].CertName { + if maxExpirationDate.Before(cert.NotAfter) { + certExists = true + maxExpirationDate = cert.NotAfter + iisBindings[i].CertHash = cert.Thumbprint + } + } + } + if !certExists { + return nil, nil, fmt.Errorf("Failed to find cert_hash with name of '%s'", iisBindings[i].CertName) + } + } + } + + websiteConfig.Bindings = iisBindings + + if err := CreateWebsite(&websiteConfig); err != nil { + d.logger.Error("Error in creating website: ", err) + return nil, nil, err + } + + if err := StartWebsite(websiteConfig.Name); err != nil { + d.logger.Error("Error in starting website: ", err) + return nil, nil, err + } + if err := handle.SetDriverState(&driverState); err != nil { return nil, nil, fmt.Errorf("failed to set driver state: %v", err) } d.tasks.Set(cfg.ID, h) - go h.run(&driverConfig) + go h.run() return handle, nil, nil } @@ -331,10 +472,10 @@ func (d *Driver) RecoverTask(handle *drivers.TaskHandle) error { return fmt.Errorf("failed to decode task state from handle: %v", err) } - var driverConfig TaskConfig - if err := handle.Config.DecodeDriverConfig(&driverConfig); err != nil { - return fmt.Errorf("failed to decode driver config: %v", err) - } + // var driverConfig TaskConfig + // if err := handle.Config.DecodeDriverConfig(&driverConfig); err != nil { + // return fmt.Errorf("failed to decode driver config: %v", err) + // } h := &taskHandle{ taskConfig: handle.Config, @@ -346,11 +487,12 @@ func (d *Driver) RecoverTask(handle *drivers.TaskHandle) error { userCpuStats: stats.NewCpuStats(), systemCpuStats: stats.NewCpuStats(), websiteStarted: false, + waitCh: make(chan struct{}), } d.tasks.Set(handle.Config.ID, h) - go h.run(&driverConfig) + go h.run() d.logger.Info("win_iis task driver: Task recovered successfully.") return nil } @@ -369,36 +511,12 @@ func (d *Driver) WaitTask(ctx context.Context, taskID string) (<-chan *drivers.E func (d *Driver) handleWait(ctx context.Context, handle *taskHandle, ch chan *drivers.ExitResult) { defer close(ch) - var result *drivers.ExitResult - - // Blocker code to monitor current task running status. - // On IIS task not running, set driver exit result and return. - for { - if handle.websiteStarted { - isRunning, err := isWebsiteRunning(handle.taskConfig.AllocID) - if err != nil { - result = &drivers.ExitResult{ - Err: fmt.Errorf("executor: error waiting on process: %v", err), - } - break - } - if !isRunning { - result = &drivers.ExitResult{ - ExitCode: 0, - } - break - } - } - time.Sleep(time.Second * 5) - } - - for { - select { - case <-ctx.Done(): - return - case <-d.ctx.Done(): - return - case ch <- result: + select { + case <-handle.waitCh: + ch <- handle.ExitResult() + case <-ctx.Done(): + ch <- &drivers.ExitResult{ + Err: ctx.Err(), } } } diff --git a/iis/handle.go b/iis/handle.go index fb636e7..4a30f72 100644 --- a/iis/handle.go +++ b/iis/handle.go @@ -19,15 +19,12 @@ package iis import ( "context" - "errors" "fmt" - "path/filepath" "sync" "time" "github.com/hashicorp/go-hclog" "github.com/hashicorp/nomad/client/stats" - shelpers "github.com/hashicorp/nomad/helper/stats" "github.com/hashicorp/nomad/plugins/drivers" ) @@ -43,6 +40,7 @@ type taskHandle struct { procState drivers.TaskState startedAt time.Time completedAt time.Time + waitCh chan struct{} exitResult *drivers.ExitResult totalCpuStats *stats.CpuStats userCpuStats *stats.CpuStats @@ -50,6 +48,12 @@ type taskHandle struct { websiteStarted bool } +func (h *taskHandle) ExitResult() *drivers.ExitResult { + h.stateLock.Lock() + defer h.stateLock.Unlock() + return h.exitResult.Copy() +} + func (h *taskHandle) TaskStatus() *drivers.TaskStatus { h.stateLock.RLock() defer h.stateLock.RUnlock() @@ -70,104 +74,48 @@ func (h *taskHandle) IsRunning() bool { return h.procState == drivers.TaskStateRunning } -func (h *taskHandle) run(driverConfig *TaskConfig) { - // Every executor runs this init at creation for stats - if err := shelpers.Init(); err != nil { - h.logger.Error("unable to initialize stats", "error", err) - } - - websiteConfig := WebsiteConfig{ - Name: h.taskConfig.AllocID, - Env: map[string]string{}, - AppPoolIdentity: iisAppPoolIdentity{ - Identity: driverConfig.AppPoolIdentity, - }, - AppPoolConfigPath: driverConfig.AppPoolConfigPath, - SiteConfigPath: driverConfig.SiteConfigPath, - } - - // Setup environment variables. - // NOMAD_APPPOOL_* are keywords for applying user/pass info for a given Application Pool in a secure manner - for key, val := range h.taskConfig.Env { - switch key { - case "NOMAD_APPPOOL_USERNAME": - websiteConfig.AppPoolIdentity.Identity = "SpecificUser" - websiteConfig.AppPoolIdentity.Username = val - case "NOMAD_APPPOOL_PASSWORD": - websiteConfig.AppPoolIdentity.Password = val - default: - websiteConfig.Env[key] = val - } - } - - if !filepath.IsAbs(driverConfig.Path) { - websiteConfig.Path = filepath.Join(h.taskConfig.TaskDir().Dir, driverConfig.Path) - } else { - websiteConfig.Path = driverConfig.Path - } - - var iisBindings []iisBinding - // If any bindings were specified, we move forward with port label cross lookups - if len(driverConfig.Bindings) > 0 { - if h.taskConfig.Resources.Ports != nil { - // parse group/shared resource ports. This is the preferred route for establishing network ports - // here is the relevant PR for the docker driver that drove this change: https://github.com/hashicorp/nomad/pull/8623 - - for _, binding := range driverConfig.Bindings { - if port, ok := h.taskConfig.Resources.Ports.Get(binding.PortLabel); ok { - binding.Port = port.Value - iisBindings = append(iisBindings, binding) - } else { - errMsg := fmt.Sprintf("Port %s not found, check network stanza", binding.PortLabel) - h.handleError(errMsg, errors.New(errMsg)) - return - } - } - } else if len(h.taskConfig.Resources.NomadResources.Networks) > 0 { - // parses a task's network stanza for dynamic/static ports - // this is deprecated as of Nomad v1.0+, in time this should be removed - // just like the docker driver, you can only work with one network stanza format over another - - for _, binding := range driverConfig.Bindings { - foundPort := false - for _, network := range h.taskConfig.Resources.NomadResources.Networks { - - for _, port := range network.ReservedPorts { - binding.Port = port.Value - iisBindings = append(iisBindings, binding) - foundPort = true - } - - for _, port := range network.DynamicPorts { - binding.Port = port.Value - iisBindings = append(iisBindings, binding) - foundPort = true - } - } - if !foundPort { - errMsg := fmt.Sprintf("Port %s not found, check network stanza", binding.PortLabel) - h.handleError(errMsg, errors.New(errMsg)) - return - } - } +func (h *taskHandle) run() { + // for { + // if isRunning, err := IsWebsiteRunning(h.taskConfig.AllocID); err != nil { + // h.logger.Error("failed to wait for website; already terminated") + // } else if !isRunning { + // h.procState = drivers.TaskStateExited + // } + // } + + //h.websiteStarted = true + + // Blocker code to monitor current task running status. + // On IIS task not running, set driver exit result and return. + var isRunning bool + var err error + for { + isRunning, err = IsWebsiteRunning(h.taskConfig.AllocID) + if err != nil || !isRunning { + // result = &drivers.ExitResult{ + // Err: fmt.Errorf("executor: error waiting on process: %v", err), + // } + break } + // if !isRunning { + // result = &drivers.ExitResult{ + // ExitCode: 0, + // } + // break + // } + time.Sleep(time.Second * 5) } - websiteConfig.Bindings = iisBindings - - if err := createWebsite(&websiteConfig); err != nil { - errMsg := fmt.Sprintf("Error in creating website: %v", err) - h.handleError(errMsg, err) - return - } - - if err := startWebsite(websiteConfig.Name); err != nil { - errMsg := fmt.Sprintf("Error in starting website: %v", err) - h.handleError(errMsg, err) - return + // Set the result + // IIS doesn't emit exit results on a site stopping. Maybe find a different solution to help provide why IIS has stopped. + h.stateLock.Lock() + h.exitResult = &drivers.ExitResult{ + ExitCode: 0, + Signal: 0, + Err: err, } - - h.websiteStarted = true + h.stateLock.Unlock() + close(h.waitCh) } // handleError will log the error message (errMsg) and update the task handle with exit results. @@ -198,7 +146,7 @@ func (h *taskHandle) handleStats(ch chan *drivers.TaskResourceUsage, ctx context } // Get IIS Worker Process stats if we can. - stats, err := getWebsiteStats(h.taskConfig.AllocID) + stats, err := GetWebsiteStats(h.taskConfig.AllocID) if err != nil { h.logger.Error("Failed to get iis worker process stats:", "error", err) return @@ -213,7 +161,7 @@ func (h *taskHandle) handleStats(ch chan *drivers.TaskResourceUsage, ctx context } // Convert IIS WMI Tasks Info to driver TaskResourceUsage expected input -func (h *taskHandle) getTaskResourceUsage(stats *wmiProcessStats) *drivers.TaskResourceUsage { +func (h *taskHandle) getTaskResourceUsage(stats *WmiProcessStats) *drivers.TaskResourceUsage { totalPercent := h.totalCpuStats.Percent(float64(stats.KernelModeTime + stats.UserModeTime)) cs := &drivers.CpuStats{ SystemMode: h.systemCpuStats.Percent(float64(stats.KernelModeTime)), @@ -242,18 +190,18 @@ func (h *taskHandle) shutdown(timeout time.Duration) error { h.stateLock.Lock() defer h.stateLock.Unlock() - if err := stopWebsite(h.taskConfig.AllocID); err != nil { + if err := StopWebsite(h.taskConfig.AllocID); err != nil { return err } - // Sleep for timeout duration to allow stopWebsite to finish gracefully. + // Sleep for timeout duration to allow StopWebsite to finish gracefully. time.Sleep(timeout) return nil } func (h *taskHandle) cleanup() error { - err := deleteWebsite(h.taskConfig.AllocID) + err := DeleteWebsite(h.taskConfig.AllocID) if err != nil { return fmt.Errorf("Error in destroying website: %v", err) } diff --git a/iis/iis.go b/iis/iis.go index 1ae87c5..e7d90ef 100644 --- a/iis/iis.go +++ b/iis/iis.go @@ -20,6 +20,7 @@ package iis import ( "bufio" "bytes" + "encoding/json" "encoding/xml" "fmt" "os/exec" @@ -27,6 +28,7 @@ import ( "strconv" "strings" "sync" + "time" wmi "github.com/StackExchange/wmi" ) @@ -158,6 +160,7 @@ type iisAppPoolIdentity struct { // IIS Binding struct to match type iisBinding struct { + CertName string `codec:"cert_name"` CertHash string `codec:"cert_hash"` HostName string `codec:"hostname"` IPAddress string `codec:"ipaddress"` @@ -167,14 +170,14 @@ type iisBinding struct { } // Stat fields that are unmarshalled from WMI -type wmiProcessStats struct { +type WmiProcessStats struct { KernelModeTime uint64 UserModeTime uint64 WorkingSetPrivate uint64 } // A Version Struct to parse IIS Version strings for granular control with features. -type iisVersion struct { +type IISVersion struct { Major int Minor int Build int @@ -196,7 +199,7 @@ func getVersionStr() (string, error) { } // Gets a version object of InetMgr.exe which parses major.minor.build.revision string -func getVersion() (*iisVersion, error) { +func GetVersion() (*IISVersion, error) { versionStr, err := getVersionStr() if err != nil { return nil, fmt.Errorf("Failed to get version string for iisVersion parsing: %v", err) @@ -206,7 +209,7 @@ func getVersion() (*iisVersion, error) { if len(versionNumbers) != 4 { return nil, fmt.Errorf("Format of IIS version is improper. It must have \"major.minor.build.revision\" format") } - version := &iisVersion{} + version := &IISVersion{} major, err := strconv.Atoi(versionNumbers[0]) if err != nil { @@ -236,7 +239,7 @@ func getVersion() (*iisVersion, error) { } // Returns if the IIS service is running in Windows Service Controller (SC) -func isIISRunning() (bool, error) { +func IsIISRunning() (bool, error) { cmd := exec.Command(`C:\Windows\System32\sc.exe`, "query", "w3svc") if out, err := cmd.CombinedOutput(); err != nil { return false, err @@ -246,7 +249,7 @@ func isIISRunning() (bool, error) { } // Removes all Application Pools and Sites from IIS -func purgeIIS() error { +func PurgeIIS() error { if sites, err := getSites(); err != nil { return err } else { @@ -269,8 +272,8 @@ func purgeIIS() error { } // Starts the IIS service in Windows SC -func startIIS() error { - if isRunning, err := isIISRunning(); err != nil || isRunning { +func StartIIS() error { + if isRunning, err := IsIISRunning(); err != nil || isRunning { return err } @@ -282,8 +285,8 @@ func startIIS() error { } // Stops the IIS service in Windows SC -func stopIIS() error { - if isRunning, err := isIISRunning(); err != nil || !isRunning { +func StopIIS() error { + if isRunning, err := IsIISRunning(); err != nil || !isRunning { return err } @@ -357,14 +360,14 @@ func applyAppPoolEnvVars(appPoolName string, envVars map[string]string) error { return nil } - if iisVersion, err := getVersion(); err != nil { + if iisVersion, err := GetVersion(); err != nil { return err } else if iisVersion.Major < 10 { // Default behavior for older versions of IIS does not accept env vars return nil } - appPool, err := getAppPool(appPoolName, true) + appPool, err := GetAppPool(appPoolName, true) if err != nil || appPool == nil { return err } @@ -443,7 +446,7 @@ func deleteAppPoolEnvVar(appPoolName string, key string) error { // Returns if an Application Pool with the given name exists in IIS func doesAppPoolExist(appPoolName string) (bool, error) { - if appPool, err := getAppPool(appPoolName, false); err != nil || appPool == nil { + if appPool, err := GetAppPool(appPoolName, false); err != nil || appPool == nil { return false, err } return true, nil @@ -465,7 +468,7 @@ func doesAppPoolEnvVarExistWithSameValue(appPool *appCmdAppPool, key string, val } // Returns an Application Pool with the given name -func getAppPool(appPoolName string, allConfigs bool) (*appCmdAppPool, error) { +func GetAppPool(appPoolName string, allConfigs bool) (*appCmdAppPool, error) { args := []string{"list", "apppool", appPoolName} if allConfigs { args = append(args, "/config:*") @@ -491,7 +494,7 @@ func getAppPools() ([]appCmdAppPool, error) { // Returns if an Application Pool with the given name is started in IIS func isAppPoolStarted(appPoolName string) (bool, error) { - if appPool, err := getAppPool(appPoolName, false); err != nil || appPool == nil { + if appPool, err := GetAppPool(appPoolName, false); err != nil || appPool == nil { return false, err } else { return strings.ToLower(appPool.State) == "started", nil @@ -526,7 +529,7 @@ func stopAppPool(appPoolName string) error { // Applies the Site bindings func applySiteBindings(siteName string, bindings []iisBinding) error { - site, err := getSite(siteName, false) + site, err := GetSite(siteName, false) if err != nil { return err } @@ -559,7 +562,6 @@ func applySiteBindings(siteName string, bindings []iisBinding) error { if !exists { addBindings = append(addBindings, binding) - } } @@ -646,7 +648,7 @@ func deleteSite(siteName string) error { // Returns if a Site with the given name exists in IIS func doesSiteExist(siteName string) (bool, error) { - if site, err := getSite(siteName, false); err != nil || site == nil { + if site, err := GetSite(siteName, false); err != nil || site == nil { return false, err } @@ -683,7 +685,7 @@ func (site *appCmdSite) getBindings() ([]iisBinding, error) { } // Returns a Site with the given name -func getSite(siteName string, allConfigs bool) (*appCmdSite, error) { +func GetSite(siteName string, allConfigs bool) (*appCmdSite, error) { args := []string{"list", "site", siteName} if allConfigs { args = append(args, "/config:*") @@ -709,7 +711,7 @@ func getSites() ([]appCmdSite, error) { // Returns if a Site with the given name is started in IIS func isSiteStarted(siteName string) (bool, error) { - if site, err := getSite(siteName, false); err != nil || site == nil { + if site, err := GetSite(siteName, false); err != nil || site == nil { return false, err } else { return strings.ToLower(site.State) == "started", nil @@ -852,7 +854,7 @@ func setVDir(appName string, path string, physicalPath string) error { } // Creates an Application Pool and Site with the given configuration -func createWebsite(websiteConfig *WebsiteConfig) error { +func CreateWebsite(websiteConfig *WebsiteConfig) error { mux.Lock() defer mux.Unlock() @@ -889,7 +891,7 @@ func createWebsite(websiteConfig *WebsiteConfig) error { } // Deletes an Application Pool and Site with the given name -func deleteWebsite(websiteName string) error { +func DeleteWebsite(websiteName string) error { if err := deleteSite(websiteName); err != nil { return err } @@ -897,7 +899,7 @@ func deleteWebsite(websiteName string) error { } // Returns if both Application Pool and Site exist with the given name -func doesWebsiteExist(websiteName string) (bool, error) { +func DoesWebsiteExist(websiteName string) (bool, error) { if exists, err := doesAppPoolExist(websiteName); err != nil || !exists { return false, err } @@ -953,14 +955,14 @@ type win32Process struct { } // Gets the WMI CPU and Memory stats of a given website -func getWebsiteStats(websiteName string) (*wmiProcessStats, error) { +func GetWebsiteStats(websiteName string) (*WmiProcessStats, error) { // Get a list of process ids tied to the app pool processIds, err := getWebsiteProcessIdsStr(websiteName) if err != nil { return nil, err } - stats := &wmiProcessStats{ + stats := &WmiProcessStats{ WorkingSetPrivate: 0, KernelModeTime: 0, UserModeTime: 0, @@ -1005,7 +1007,7 @@ func getWebsiteStats(websiteName string) (*wmiProcessStats, error) { return stats, nil } -func isWebsiteStarted(websiteName string) (bool, error) { +func IsWebsiteStarted(websiteName string) (bool, error) { if isStarted, err := isAppPoolStarted(websiteName); err != nil || !isStarted { return false, err } @@ -1017,7 +1019,7 @@ func isWebsiteStarted(websiteName string) (bool, error) { } // Returns if the Application Pool has running processes or both Application Pool and Site are started with the given name -func isWebsiteRunning(websiteName string) (bool, error) { +func IsWebsiteRunning(websiteName string) (bool, error) { processIds, err := getWebsiteProcessIdsStr(websiteName) if err != nil { return false, err @@ -1026,7 +1028,7 @@ func isWebsiteRunning(websiteName string) (bool, error) { return true, nil } - if isRunning, err := isWebsiteStarted(websiteName); err != nil || !isRunning { + if isRunning, err := IsWebsiteStarted(websiteName); err != nil || !isRunning { return false, err } @@ -1034,7 +1036,7 @@ func isWebsiteRunning(websiteName string) (bool, error) { } // Starts both Application Pool and Site with the given name -func startWebsite(websiteName string) error { +func StartWebsite(websiteName string) error { if err := startAppPool(websiteName); err != nil { return err } @@ -1042,7 +1044,7 @@ func startWebsite(websiteName string) error { } // Stops both Application Pool and Site with the given name -func stopWebsite(websiteName string) error { +func StopWebsite(websiteName string) error { if err := stopSite(websiteName); err != nil { return err } @@ -1057,6 +1059,27 @@ func getNetshIP(ipAddress string) string { } } +type IISCert struct { + CN string `json:"CN"` + FriendlyName string `json:"FriendlyName"` + NotAfter time.Time `json:"NotAfter"` + Thumbprint string `json:"Thumbprint"` +} + +func getIISCerts() ([]IISCert, error) { + var certs []IISCert + ps_script := `ConvertTo-Json @(Get-ChildItem cert:\LocalMachine\My | select -Property Thumbprint, Subject,FriendlyName, @{name='NotAfter'; expression= {$_.NotAfter.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK")}}, @{name='CN'; expression= {$_.Subject.split(",")[0].Substring(3)}})` + cmd := exec.Command("powershell.exe", "-NoProfile", "-NonInteractive", "-Command", ps_script) + + out, err := cmd.Output() + if err != nil { + return certs, fmt.Errorf("Failed to gather certs: %+v", err) + } + + err = json.Unmarshal(out, &certs) + return certs, err +} + // Binds an appid, ip address, and port to a hash of a pre-existing certificate in the cert store for https protocol IIS binding with netsh func bindSSLCert(appID string, ipAddress string, port int, hash string) error { if info, err := getSSLCertBinding(ipAddress, port); err != nil { diff --git a/iis/iis_test.go b/iis/iis_test.go index e461d18..1f0541f 100644 --- a/iis/iis_test.go +++ b/iis/iis_test.go @@ -60,7 +60,7 @@ var ( } ) -// Test the fingerprinting ability of getVersion to ensure it is outputing the proper version format of IIS +// Test the fingerprinting ability of GetVersion to ensure it is outputing the proper version format of IIS func TestIISVersion(t *testing.T) { version, err := getVersionStr() if err != nil { @@ -76,7 +76,7 @@ func TestIISVersion(t *testing.T) { // Test to ensure IIS functions for altering IIS's state works for other functional/integration tests func TestIISRunning(t *testing.T) { - if err := stopIIS(); err != nil { + if err := StopIIS(); err != nil { t.Fatal(err) } @@ -86,7 +86,7 @@ func TestIISRunning(t *testing.T) { isRunning := true for isRunning { - if running, err := isIISRunning(); err != nil { + if running, err := IsIISRunning(); err != nil { t.Fatal(err) } else { isRunning = running @@ -107,7 +107,7 @@ func TestIISRunning(t *testing.T) { t.Fatal("Timeout: IIS failed to stop in a reasonable time!") } - if err := startIIS(); err != nil { + if err := StartIIS(); err != nil { t.Fatal("Error trying to start IIS!") } @@ -116,7 +116,7 @@ func TestIISRunning(t *testing.T) { isRunning := false for !isRunning { - if running, err := isIISRunning(); err != nil { + if running, err := IsIISRunning(); err != nil { t.Fatal(err) } else { isRunning = running @@ -191,7 +191,7 @@ func TestSSLBinding(t *testing.T) { // Helper function for verify iis bindings match func doBindingsMatchSite(t *testing.T, expected []iisBinding, siteName string) bool { - site, err := getSite(guid, true) + site, err := GetSite(guid, true) if err != nil { t.Fatal(err) } else if site == nil { @@ -234,7 +234,7 @@ func doBindingsMatchSite(t *testing.T, expected []iisBinding, siteName string) b // Test various bindings that could be applied to a Site func TestSiteBinding(t *testing.T) { // Clean up pre-exisint IIS sites - if err := purgeIIS(); err != nil { + if err := PurgeIIS(); err != nil { t.Fatal("Error purging: ", err) } @@ -300,24 +300,24 @@ func TestWebsite(t *testing.T) { assert := assert.New(t) // Clean any pre-existing websites - if err := purgeIIS(); err != nil { + if err := PurgeIIS(); err != nil { t.Fatal("Error purging: ", err) } // Create a website with the config and website name - if err := createWebsite(&websiteConfig); err != nil { + if err := CreateWebsite(&websiteConfig); err != nil { t.Fatal(err) } websiteConfig.Env["EXAMPLE_ENV_VAR_ALT"] = "test789" // Ensure create website is idempotent - if err := createWebsite(&websiteConfig); err != nil { + if err := CreateWebsite(&websiteConfig); err != nil { t.Fatal(err) } // Verify app pool settings match with given config - if appPool, err := getAppPool(guid, true); err != nil { + if appPool, err := GetAppPool(guid, true); err != nil { t.Fatal("Failed to get Site info!") } else { assert.Equal(websiteConfig.AppPoolIdentity.Identity, appPool.Add.ProcessModel.IdentityType, "AppPool Identity Type doesn't match!") @@ -325,7 +325,7 @@ func TestWebsite(t *testing.T) { assert.Equal(websiteConfig.AppPoolIdentity.Password, appPool.Add.ProcessModel.Password, "AppPool Identity Password doesn't match!") // Verify env vars are properly set for both altered and non-altered env vars for IIS 10+ - if iisVersion, err := getVersion(); err != nil { + if iisVersion, err := GetVersion(); err != nil { t.Fatal(err) } else if iisVersion.Major >= 10 { expectedAppPoolEnvVars := []appPoolAddEnvVar{ @@ -339,24 +339,24 @@ func TestWebsite(t *testing.T) { } // Verify that site settings match the given config - if site, err := getSite(guid, true); err != nil { + if site, err := GetSite(guid, true); err != nil { t.Fatal("Failed to get Site info!") } else { assert.Equal(site.Site.Application.VDirs[0].PhysicalPath, websiteConfig.Path, "Website path doesn't match desired path from config!") } // Start the website - if err := startWebsite(guid); err != nil { + if err := StartWebsite(guid); err != nil { t.Fatal(err) } // Ensure start website is idempotent - if err := startWebsite(guid); err != nil { + if err := StartWebsite(guid); err != nil { t.Fatal(err) } // Verify that the website is running - if isRunning, err := isWebsiteRunning(guid); err != nil { + if isRunning, err := IsWebsiteRunning(guid); err != nil { t.Fatal(err) } else if !isRunning { t.Fatal("Website is not started!") @@ -380,7 +380,7 @@ func TestWebsite(t *testing.T) { } // Verify that the website is deemed as not running - if isRunning, err := isWebsiteRunning(guid); err != nil { + if isRunning, err := IsWebsiteRunning(guid); err != nil { t.Fatal(err) } else if isRunning { t.Fatal("Website is still running when Site is stopped!") @@ -392,7 +392,7 @@ func TestWebsite(t *testing.T) { } // Verify that the website is deemed as running again - if isRunning, err := isWebsiteRunning(guid); err != nil { + if isRunning, err := IsWebsiteRunning(guid); err != nil { t.Fatal(err) } else if !isRunning { t.Fatal("Website is not running when Site was started!") @@ -404,7 +404,7 @@ func TestWebsite(t *testing.T) { } // Verify that the website is deemed as not running - if isRunning, err := isWebsiteRunning(guid); err != nil { + if isRunning, err := IsWebsiteRunning(guid); err != nil { t.Fatal(err) } else if isRunning { t.Fatal("Website is still running when AppPool is stopped!") @@ -416,41 +416,41 @@ func TestWebsite(t *testing.T) { } // Verify that the website is deemed as running again - if isRunning, err := isWebsiteRunning(guid); err != nil { + if isRunning, err := IsWebsiteRunning(guid); err != nil { t.Fatal(err) } else if !isRunning { t.Fatal("Website is not running when AppPool was started!") } // Stop the website - if err := stopWebsite(guid); err != nil { + if err := StopWebsite(guid); err != nil { t.Fatal(err) } // Ensure stop website is idempotent - if err := stopWebsite(guid); err != nil { + if err := StopWebsite(guid); err != nil { t.Fatal(err) } // Verify that the website is not running - if isRunning, err := isWebsiteRunning(guid); err != nil { + if isRunning, err := IsWebsiteRunning(guid); err != nil { t.Fatal(err) } else if isRunning { t.Fatal("Website is not stopped!") } // Delete the website - if err := deleteWebsite(guid); err != nil { + if err := DeleteWebsite(guid); err != nil { t.Fatal(err) } // Ensure delete website is idempotent - if err := deleteWebsite(guid); err != nil { + if err := DeleteWebsite(guid); err != nil { t.Fatal(err) } // Verify that the website is deleted - if exists, err := doesWebsiteExist(guid); err != nil { + if exists, err := DoesWebsiteExist(guid); err != nil { t.Fatal(err) } else { assert.False(exists, "Website exists after deletion!") @@ -463,7 +463,7 @@ func TestWebsiteWithConfig(t *testing.T) { assert := assert.New(t) // Clean any pre-existing websites - if err := purgeIIS(); err != nil { + if err := PurgeIIS(); err != nil { t.Fatal("Error purging: ", err) } @@ -481,14 +481,14 @@ func TestWebsiteWithConfig(t *testing.T) { websiteConfig.SiteConfigPath = filepath.Join(parentDir, "test", "testsite.xml") // Create a website with the config and website name - if err := createWebsite(&websiteConfig); err != nil { + if err := CreateWebsite(&websiteConfig); err != nil { t.Fatal(err) } websiteConfig.Env["EXAMPLE_ENV_VAR_ALT"] = "test789" // Verify app pool settings match with given config - if appPool, err := getAppPool(guid, true); err != nil { + if appPool, err := GetAppPool(guid, true); err != nil { t.Fatal("Failed to get Site info!") } else { assert.Equal("ApplicationPoolIdentity", appPool.Add.ProcessModel.IdentityType, "AppPool Identity Type doesn't match!") @@ -500,7 +500,7 @@ func TestWebsiteWithConfig(t *testing.T) { assert.Equal("Integrated", appPool.PipelineMode, "AppPool PipelineMode doesn't match!") // Verify env vars are properly set for both altered and non-altered env vars for IIS 10+ - if iisVersion, err := getVersion(); err != nil { + if iisVersion, err := GetVersion(); err != nil { t.Fatal(err) } else if iisVersion.Major >= 10 { expectedAppPoolEnvVars := []appPoolAddEnvVar{ @@ -514,26 +514,26 @@ func TestWebsiteWithConfig(t *testing.T) { } // Verify that site settings match the given config - if site, err := getSite(guid, true); err != nil { + if site, err := GetSite(guid, true); err != nil { t.Fatal("Failed to get Site info!") } else { assert.Equal(site.Site.Application.VDirs[0].PhysicalPath, websiteConfig.Path, "Website path doesn't match desired path from config!") } // Start the website - if err := startWebsite(guid); err != nil { + if err := StartWebsite(guid); err != nil { t.Fatal(err) } // Verify that the website is running - if isRunning, err := isWebsiteRunning(guid); err != nil { + if isRunning, err := IsWebsiteRunning(guid); err != nil { t.Fatal(err) } else if !isRunning { t.Fatal("Website is not started!") } // Gather stats of the website's worker processes - stats, err := getWebsiteStats(guid) + stats, err := GetWebsiteStats(guid) if err != nil { t.Fatal(err) } @@ -544,24 +544,24 @@ func TestWebsiteWithConfig(t *testing.T) { assert.NotEqual(stats.UserModeTime, 0, "UserModeTime returned 0!") // Stop the website - if err := stopWebsite(guid); err != nil { + if err := StopWebsite(guid); err != nil { t.Fatal(err) } // Verify that the website is not running - if isRunning, err := isWebsiteRunning(guid); err != nil { + if isRunning, err := IsWebsiteRunning(guid); err != nil { t.Fatal(err) } else if isRunning { t.Fatal("Website is not stopped!") } // Delete the website - if err := deleteWebsite(guid); err != nil { + if err := DeleteWebsite(guid); err != nil { t.Fatal(err) } // Verify that the website is deleted - if exists, err := doesWebsiteExist(guid); err != nil { + if exists, err := DoesWebsiteExist(guid); err != nil { t.Fatal(err) } else { assert.False(exists, "Website exists after deletion!") diff --git a/scripts/win_provision.ps1 b/scripts/win_provision.ps1 index 975a8bc..16faebf 100644 --- a/scripts/win_provision.ps1 +++ b/scripts/win_provision.ps1 @@ -1,19 +1,21 @@ Set-ExecutionPolicy Bypass -Scope Process -Force +if($env:CI -ne 'true') { Set-Location -Path 'C:\\vagrant' } [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) choco install git.install --version=2.25.1 -y --no-progress choco install golang --version=1.15 -y --no-progress choco install nomad --version=1.0.4 -y --no-progress +choco install pester --version=5.1.1 -y --no-progress Stop-Service nomad Get-CimInstance win32_service -filter "name='nomad'" | Invoke-CimMethod -Name Change -Arguments @{StartName="LocalSystem"} | Out-Null $nomadDir = "C:\\ProgramData\\nomad" New-Item -ItemType Directory -Path "$nomadDir\\plugin" -Force -Copy-Item "C:\\vagrant\\win_iis.exe" -Destination "$nomadDir\\plugin" -Force -Copy-Item "C:\\vagrant\\test\\win_client.hcl" -Destination "$nomadDir\\conf\\client.hcl" -Force +Copy-Item ".\\win_iis.exe" -Destination "$nomadDir\\plugin" -Force +Copy-Item ".\\test\\win_client.hcl" -Destination "$nomadDir\\conf\\client.hcl" -Force Start-Service nomad -Import-PfxCertificate -FilePath "C:\\vagrant\\test\\test.pfx" -CertStoreLocation Cert:\\LocalMachine\\My -Password (ConvertTo-SecureString -String 'Test123!' -AsPlainText -Force) +Import-PfxCertificate -FilePath ".\\test\\test.pfx" -CertStoreLocation Cert:\\LocalMachine\\My -Password (ConvertTo-SecureString -String 'Test123!' -AsPlainText -Force) Install-WindowsFeature -Name Web-Server -IncludeAllSubFeature -IncludeManagementTools -Restart diff --git a/test/e2e/Default.Tests.ps1 b/test/e2e/Default.Tests.ps1 new file mode 100644 index 0000000..6f72d39 --- /dev/null +++ b/test/e2e/Default.Tests.ps1 @@ -0,0 +1,345 @@ +Describe "PreReqs" { + It "Nomad Installed" { + $cmd = Get-Command "nomad.exe" -ErrorAction SilentlyContinue + $cmd | Should -Not -BeNullOrEmpty -ErrorAction Stop + } + + It "Nomad Driver Installed" { + $driverFile = Get-Item "C:\ProgramData\nomad\plugin\win_iis.exe" -ErrorAction SilentlyContinue + $driverFile | Should -Not -BeNullOrEmpty + } + + It "IIS Installed" { + $state = (Get-WindowsFeature Web-Server).InstallState + $state | Should -Be "Installed" + } + + It "Nomad Running" { + $service = Get-Service "nomad" + $service | Should -Not -BeNullOrEmpty + $service.Status | Should -Be "Running" + } +} + +Describe "TestE2E" { + BeforeAll { + Import-Module WebAdministration + } + BeforeEach { + # Clean Nomad Jobs + $jobsContent = Invoke-RestMethod -Uri "http://localhost:4646/v1/jobs" -Method GET + if ($jobsContent) { + $jobsContent | ForEach-Object { + $url = "http://localhost:4646/v1/job/" + $_.ID + '?purge=true'; + #$bodyParams = @{purge=$true} | ConvertTo-Json; + Invoke-RestMethod -Uri $url -Method DELETE }; + } + # # Wait for all jobs to stop + # $timeout = New-TimeSpan -Seconds 15 + # $endTime = (Get-Date).Add($timeout) + # $allStopped = $false + # # Nomad System GC will not release jobs in these statuses + # $runningStatuses = @('pending', 'running') + # do { + # $allStopped = $true + # $jobsContent = Invoke-RestMethod -Uri "http://localhost:4646/v1/jobs" -Method GET + # Write-Host $jobsContent + # if ($jobsContent) { + # foreach ($job in $jobsContent) { + # Write-Host $job + # if ($runningStatuses.Contains($job.Status.ToLower())) { $allStopped = $false } + # } + # } + # Write-Host $allStopped + # } + # until ($allStopped -or ((Get-Date) -gt $endTime)) + # $allStopped | Should -Be $true + # # Perform Nomad GC to clear all jobs from existence + # # Nomad isn't keen to destroying jobs immediately after they are stopped + # Start-Sleep -s 10 + & "nomad.exe" "system" "gc" + + # Clean IIS + Start-Service "w3svc" + #Get-Process "w3wp" -ErrorAction SilentlyContinue | Stop-Process -Confirm:$false + Get-Website | Remove-Website -ErrorAction SilentlyContinue + Get-IISAppPool | Remove-WebAppPool -ErrorAction SilentlyContinue + Reset-IISServerManager -Confirm:$false + } + + It "Verify Nomad and Websites Are Empty" { + $jobsContent = Invoke-RestMethod -Uri "http://localhost:4646/v1/jobs" -Method GET + $jobsContent | Should -BeNullOrEmpty + + Get-Website | Should -BeNullOrEmpty + Get-IISAppPool | Should -BeNullOrEmpty + } + + It "Test Fingerprinting" { + # Waiting for health of the driver to become "True" before starting tests + # All fingerprinting state changing in nomad takes about 45s to fullfill + $timeout = New-TimeSpan -Seconds 45 + $endTime = (Get-Date).Add($timeout) + do { + $result = (& "nomad.exe" "node" "status" "-json" | Out-String | ConvertFrom-Json)[0].Drivers.win_iis.Healthy + } + until ($result -eq $true -or ((Get-Date) -gt $endTime)) + $result | Should -Be $true + + $driver = (& "nomad.exe" "node" "status" "-json" | Out-String | ConvertFrom-Json)[0].Drivers.win_iis + + $driver.Attributes | Select-Object -ExpandProperty "driver.win_iis.version" | Should -Match "^[0-9]*\.[0-9]*\.[0-9]*$" + $driver.Attributes | Select-Object -ExpandProperty "driver.win_iis.iis_version" | Should -Match "^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$" + + # Stopping IIS + Stop-Service "w3svc" + + # Waiting for health of the driver to become "False" + $endTime = (Get-Date).Add($timeout) + do { + $result = (& "nomad.exe" "node" "status" "-json" | Out-String | ConvertFrom-Json)[0].Drivers.win_iis.Healthy + } + until ($result -eq $false -or ((Get-Date) -gt $endTime)) + $result | Should -Be $false + + # Stopping IIS + Start-Service "w3svc" + + # Waiting for health of the driver to become "False" + $endTime = (Get-Date).Add($timeout) + do { + $result = (& "nomad.exe" "node" "status" "-json" | Out-String | ConvertFrom-Json)[0].Drivers.win_iis.Healthy + } + until ($result -eq $true -or ((Get-Date) -gt $endTime)) + $result | Should -Be $true + } + + It "Nomad Job Kill Site" { + & "nomad.exe" "job" "run" "C:\vagrant\examples\iis-test.nomad" + + $job = $null + # Wait for the job to be placed + $timeout = New-TimeSpan -Seconds 30 + $endTime = (Get-Date).Add($timeout) + $isRunning = $false + do { + $job = Invoke-RestMethod -Uri "http://localhost:4646/v1/job/iis-test" -Method GET + if ($job -and $job.Status.ToLower() -eq 'running') { + $isRunning = $true + } + } + until ($isRunning -or ((Get-Date) -gt $endTime)) + $isRunning | Should -Be $true + + $allocs = Invoke-RestMethod -Uri "http://localhost:4646/v1/job/iis-test/allocations" -Method GET + $allocs | Should -Not -BeNullOrEmpty + + $guid = $allocs[-1].ID + + Reset-IISServerManager -Confirm:$false + $site = Get-IISSite $guid + $appPool = Get-IISAppPool $guid + + $site | Should -Not -BeNullOrEmpty + $appPool | Should -Not -BeNullOrEmpty + + Stop-IISSite $guid -Confirm:$false + + # Wait for the job to stop running + $endTime = (Get-Date).Add($timeout) + do { + $job = Invoke-RestMethod -Uri "http://localhost:4646/v1/job/iis-test" -Method GET + if ($job -and $job.Status.ToLower() -ne 'running') { + $isRunning = $false + } + } + until (!$isRunning -or ((Get-Date) -gt $endTime)) + $isRunning | Should -Be $false + } + + It "Nomad Job Kill App Pool" { + & "nomad.exe" "job" "run" "C:\vagrant\examples\iis-test.nomad" + # Wait for the job to start running + $timeout = New-TimeSpan -Seconds 30 + $endTime = (Get-Date).Add($timeout) + $isRunning = $false + do { + $job = Invoke-RestMethod -Uri "http://localhost:4646/v1/job/iis-test" -Method GET + if ($job -and $job.Status.ToLower() -eq 'running') { + $isRunning = $true + } + } + until ($isRunning -or ((Get-Date) -gt $endTime)) + $isRunning | Should -Be $true + } + + It "Nomad Stop Job" { + & "nomad.exe" "job" "run" "$PSScriptRoot\..\jobs\iis-test.nomad" + + $job = $null + # Wait for the job to be placed + $timeout = New-TimeSpan -Seconds 45 + $endTime = (Get-Date).Add($timeout) + $isRunning = $false + do { + $job = Invoke-RestMethod -Uri "http://localhost:4646/v1/job/iis-test" -Method GET + if ($job -and $job.Status.ToLower() -eq 'running') { + $isRunning = $true + } + } + until ($isRunning -or ((Get-Date) -gt $endTime)) + $isRunning | Should -Be $true + + $allocs = Invoke-RestMethod -Uri "http://localhost:4646/v1/job/iis-test/allocations" -Method GET + $allocs | Should -Not -BeNullOrEmpty + + $guid = $allocs[0].ID + + Reset-IISServerManager -Confirm:$false + $site = Get-IISSite $guid + $appPool = Get-IISAppPool $guid + + $site | Should -Not -BeNullOrEmpty + $appPool | Should -Not -BeNullOrEmpty + + & "nomad.exe" "job" "stop" "iis-test" + + # Wait for the job to stop running + $endTime = (Get-Date).Add($timeout) + do { + Reset-IISServerManager -Confirm:$false + $site = Get-IISSite $guid + $appPool = Get-IISAppPool $guid + } + until ((!$site -and !$appPool) -or ((Get-Date) -gt $endTime)) + $site | Should -BeNullOrEmpty + $appPool | Should -BeNullOrEmpty + } + + It "Nomad Job Identity and Stats" { + & "nomad.exe" "job" "run" "$PSScriptRoot\..\jobs\iis-test.nomad" + + $job = $null + # Wait for the job to be placed + $timeout = New-TimeSpan -Seconds 45 + $endTime = (Get-Date).Add($timeout) + $isRunning = $false + do { + $job = Invoke-RestMethod -Uri "http://localhost:4646/v1/job/iis-test" -Method GET + if ($job -and $job.Status.ToLower() -eq 'running') { + $isRunning = $true + } + } + until ($isRunning -or ((Get-Date) -gt $endTime)) + $isRunning | Should -Be $true + + $allocs = Invoke-RestMethod -Uri "http://localhost:4646/v1/job/iis-test/allocations" -Method GET + $allocs | Should -Not -BeNullOrEmpty + + $guid = $allocs[0].ID + + Get-Item "IIS:\AppPools\$guid" | Select-Object -ExpandProperty processModel | Select-Object -expand identityType | Should -Be 'ApplicationPoolIdentity' + + # Call cpu.aspx to trigger higher cpu and mem usage + # Don't wait for the result, we only care about stat collection + Start-Job -ScriptBlock { Invoke-RestMethod -Uri "http://localhost:81/cpu.aspx" -Method GET } + + $timeout = New-TimeSpan -Seconds 45 + $endTime = (Get-Date).Add($timeout) + $cpuPercent = 0 + $memRSS = 0 + # Stat Collecting takes 5s + Start-Sleep -s 5 + do { + $allocStats = Invoke-RestMethod -Uri "http://localhost:4646/v1/client/allocation/$guid/stats" -Method GET + if ($allocStats) { + $cpuPercent = $allocStats.ResourceUsage.CpuStats.Percent + $memRSS = $allocStats.ResourceUsage.MemoryStats.RSS + } + } + until (($cpuPercent -gt 0 -and $memRSS -gt 0) -or ((Get-Date) -gt $endTime)) + $cpuPercent | Should -BeGreaterThan 0 + $memRSS | Should -BeGreaterThan 0 + } + + It "Nomad Env Var" { + & "nomad.exe" "job" "run" "$PSScriptRoot\..\jobs\iis-test.nomad" + + $job = $null + # Wait for the job to be placed + $timeout = New-TimeSpan -Seconds 45 + $endTime = (Get-Date).Add($timeout) + $isRunning = $false + do { + $job = Invoke-RestMethod -Uri "http://localhost:4646/v1/job/iis-test" -Method GET + if ($job -and $job.Status.ToLower() -eq 'running') { + $isRunning = $true + } + } + until ($isRunning -or ((Get-Date) -gt $endTime)) + $isRunning | Should -Be $true + + (Invoke-RestMethod -Uri "http://localhost:81" -Method GET).html.body | Should -Match "TestEnvVar:Test123!" + } + + It "Placement Fail: Bindings" { + & "nomad.exe" "job" "run" "$PSScriptRoot\..\jobs\bad-binding.nomad" + + $job = $null + # Wait for the job to be placed + $timeout = New-TimeSpan -Seconds 45 + $endTime = (Get-Date).Add($timeout) + $isFailed = $false + do { + $allocs = Invoke-RestMethod -Uri "http://localhost:4646/v1/allocations" -Method GET + if ($allocs -and $allocs[-1].ClientStatus.ToLower() -eq 'failed') { + $isFailed = $true + } + } + until ($isFailed -or ((Get-Date) -gt $endTime)) + $isFailed | Should -Be $true + } + + It "Placement Fail: Cert" { + & "nomad.exe" "job" "run" "$PSScriptRoot\..\jobs\bad-cert.nomad" + + $job = $null + # Wait for the job to be placed + $timeout = New-TimeSpan -Seconds 30 + $endTime = (Get-Date).Add($timeout) + $isFailed = $false + do { + $allocs = Invoke-RestMethod -Uri "http://localhost:4646/v1/allocations" -Method GET + if ($allocs -and $allocs[-1].ClientStatus.ToLower() -eq 'failed') { + $isFailed = $true + } + } + until ($isFailed -or ((Get-Date) -gt $endTime)) + $isFailed | Should -Be $true + } + + It "User Test" { + & "nomad.exe" "job" "run" "$PSScriptRoot\..\jobs\user-test.nomad" + + $job = $null + # Wait for the job to be placed + $timeout = New-TimeSpan -Seconds 45 + $endTime = (Get-Date).Add($timeout) + $isRunning = $false + do { + $allocs = Invoke-RestMethod -Uri "http://localhost:4646/v1/allocations" -Method GET + if ($allocs -and $allocs[-1].ClientStatus.ToLower() -eq 'running') { + $isRunning = $true + } + } + until ($isRunning -or ((Get-Date) -gt $endTime)) + $isRunning | Should -Be $true + + $allocs = Invoke-RestMethod -Uri "http://localhost:4646/v1/job/user-test/allocations" -Method GET + $allocs | Should -Not -BeNullOrEmpty + + $guid = $allocs[-1].ID + + Get-Item "IIS:\AppPools\$guid" | Select-Object -ExpandProperty processModel | Select-Object -expand identityType | Should -Be 'SpecificUser' + } +} diff --git a/test/e2e/env_var_test.go b/test/e2e/env_var_test.go new file mode 100644 index 0000000..05bad79 --- /dev/null +++ b/test/e2e/env_var_test.go @@ -0,0 +1,28 @@ +package e2e + +import ( + "io/ioutil" + "net/http" + "regexp" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEnvVars(t *testing.T) { + t.Cleanup(cleanup) + _, err := runTestJob("iis-test.nomad", "running", 30*time.Second) + require.Nil(t, err, "failed to run job") + + // TODO: retry/await site coming up + time.Sleep(5 * time.Second) + + resp, err := http.Get("http://localhost:81") + require.Nil(t, err, "failed to get response from iis-test index page") + + body, err := ioutil.ReadAll(resp.Body) + require.Nil(t, err, "failed to read response body bytes") + assert.Regexp(t, regexp.MustCompile(`TestEnvVar:Test123!`), string(body)) +} diff --git a/test/e2e/failed_status_test.go b/test/e2e/failed_status_test.go new file mode 100644 index 0000000..2209fdc --- /dev/null +++ b/test/e2e/failed_status_test.go @@ -0,0 +1,20 @@ +package e2e + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestBadCert(t *testing.T) { + t.Cleanup(cleanup) + _, err := runTestJobWithAllocStatus("bad-cert.nomad", "failed", 30*time.Second) + require.Nil(t, err, "failed to run job") +} + +func TestBadBindings(t *testing.T) { + t.Cleanup(cleanup) + _, err := runTestJobWithAllocStatus("bad-binding.nomad", "failed", 30*time.Second) + require.Nil(t, err, "failed to run job") +} diff --git a/test/e2e/fingerprint_test.go b/test/e2e/fingerprint_test.go new file mode 100644 index 0000000..b0a5dd6 --- /dev/null +++ b/test/e2e/fingerprint_test.go @@ -0,0 +1,90 @@ +package e2e + +import ( + "fmt" + "regexp" + "testing" + "time" + + nomad "github.com/hashicorp/nomad/api" + "github.com/roblox/nomad-driver-iis/iis" + "github.com/stretchr/testify/assert" +) + +func getNomadNode() (*nomad.NodeListStub, error) { + nodes, _, err := nomadClient.Nodes().List(nil) + if err != nil { + return nil, fmt.Errorf("Error getting NomadNode: %v", err) + } + + return nodes[0], nil +} + +func TestFingerprintKeys(t *testing.T) { + if err := iis.StopIIS(); err != nil { + t.Fatal("Error trying to stop IIS!") + } + + node, err := getNomadNode() + if err != nil { + t.Fatal(err) + } + + assert.Regexp(t, regexp.MustCompile(`^[0-9]*\.[0-9]*\.[0-9]*$`), node.Drivers["win_iis"].Attributes["driver.win_iis.version"]) + assert.Regexp(t, regexp.MustCompile(`^[0-9]*\.[0-9]*\.[0-9]*\.[[0-9]*$`), node.Drivers["win_iis"].Attributes["driver.win_iis.iis_version"]) +} + +func TestFingerprintHealth(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + t.Cleanup(cleanup) + + if err := iis.StopIIS(); err != nil { + t.Fatal("Error trying to stop IIS!") + } + + timeout := 45 * time.Second + node, err := getNomadNode() + if err != nil { + t.Fatal(err) + } + + endTime := time.Now().Add(timeout) + isHealthy := true + for { + nodeInfo, _, err := nomadClient.Nodes().Info(node.ID, nil) + if err != nil { + t.Fatal("Failed to get nomad node ", err) + } + + isHealthy = nodeInfo.Drivers["win_iis"].Healthy + + if !isHealthy || time.Now().After(endTime) { + break + } + time.Sleep(1 * time.Second) + } + assert.False(t, isHealthy) + + // Turn IIS back on and wait for health state to change + if err := iis.StartIIS(); err != nil { + t.Fatal("Error trying to start IIS!") + } + endTime = time.Now().Add(timeout) + isHealthy = false + for { + nodeInfo, _, err := nomadClient.Nodes().Info(node.ID, nil) + if err != nil { + t.Fatal("Failed to get nomad node ", err) + } + + isHealthy = nodeInfo.Drivers["win_iis"].Healthy + + if isHealthy || time.Now().After(endTime) { + break + } + time.Sleep(1 * time.Second) + } + assert.True(t, isHealthy) +} diff --git a/test/e2e/iis_user_test.go b/test/e2e/iis_user_test.go new file mode 100644 index 0000000..433e64c --- /dev/null +++ b/test/e2e/iis_user_test.go @@ -0,0 +1,31 @@ +package e2e + +import ( + "testing" + "time" + + "github.com/roblox/nomad-driver-iis/iis" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIISUser(t *testing.T) { + t.Cleanup(cleanup) + job, err := runTestJob("user-test.nomad", "running", 30*time.Second) + require.Nil(t, err, "failed to run job") + + // TODO: retry/await site coming up + time.Sleep(5 * time.Second) + + allocs, _, err := nomadClient.Jobs().Allocations(*job.ID, false, nil) + if err != nil { + t.Fatal("Error trying to job allocs:", err) + } + + assert.Greater(t, len(allocs), 0) + guid := allocs[0].ID + + appPool, err := iis.GetAppPool(guid, true) + require.Nil(t, err, "failed to get apppool info") + assert.Equal(t, "SpecificUser", appPool.Add.ProcessModel.IdentityType, "AppPool Identity Type doesn't match!") +} diff --git a/test/e2e/job_kill_test.go b/test/e2e/job_kill_test.go new file mode 100644 index 0000000..8b307a4 --- /dev/null +++ b/test/e2e/job_kill_test.go @@ -0,0 +1,29 @@ +package e2e + +import ( + "testing" + "time" + + "github.com/roblox/nomad-driver-iis/iis" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestJobStatusKillSite(t *testing.T) { + t.Cleanup(cleanup) + job, err := runTestJob("iis-test.nomad", "running", 30*time.Second) + require.Nil(t, err, "failed to run job") + + allocs, _, err := nomadClient.Jobs().Allocations(*job.ID, false, nil) + require.Nil(t, err, "error gathering job allocs") + + assert.Greater(t, len(allocs), 0) + guid := allocs[0].ID + + time.Sleep(5 * time.Second) + err = iis.StopWebsite(guid) + require.Nil(t, err, "failed to stop website") + + err = waitForAllocStatus(job, "failed", 30*time.Second) + require.Nil(t, err, "alloc did not fail after website stopped") +} diff --git a/test/e2e/job_stats_test.go b/test/e2e/job_stats_test.go new file mode 100644 index 0000000..c6d7651 --- /dev/null +++ b/test/e2e/job_stats_test.go @@ -0,0 +1,38 @@ +package e2e + +import ( + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestJobStats(t *testing.T) { + t.Cleanup(cleanup) + job, err := runTestJob("iis-test.nomad", "running", 30*time.Second) + require.Nil(t, err, "failed to run job") + + // TODO: retry/await site coming up + time.Sleep(5 * time.Second) + + // fire and forget + // there ought to be a cleaner way to handle this, will have to ask during PR + client := &http.Client{ + Timeout: 15 * time.Second, + } + req, err := http.NewRequest("GET", "http://localhost:81/cpu.aspx", nil) + require.Nil(t, err, "failed to make a request to cpu test page") + client.Do(req) + + allocs, _, err := nomadClient.Jobs().Allocations(*job.ID, false, nil) + require.Nil(t, err, "failed to get allocations for job") + alloc, _, err := nomadClient.Allocations().Info(allocs[0].ID, nil) + require.Nil(t, err, "failed to get alloc info for first allocation found") + stats, err := nomadClient.Allocations().Stats(alloc, nil) + require.Nil(t, err, "failed to get stats for allocation") + + assert.Greater(t, stats.ResourceUsage.CpuStats.Percent, float64(0)) + assert.Greater(t, stats.ResourceUsage.MemoryStats.RSS, uint64(0)) +} diff --git a/test/e2e/job_stop_test.go b/test/e2e/job_stop_test.go new file mode 100644 index 0000000..21303b8 --- /dev/null +++ b/test/e2e/job_stop_test.go @@ -0,0 +1,39 @@ +package e2e + +import ( + "fmt" + "testing" + "time" + + "github.com/roblox/nomad-driver-iis/iis" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestJobStopCleanup(t *testing.T) { + t.Cleanup(cleanup) + job, err := runTestJob("iis-test.nomad", "running", 30*time.Second) + require.Nil(t, err, "failed to run job") + + allocs, _, err := nomadClient.Jobs().Allocations(*job.ID, false, nil) + require.Nil(t, err, "error gathering job allocs") + + require.Greater(t, len(allocs), 0) + guid := allocs[0].ID + + time.Sleep(5 * time.Second) + websiteExists, err := iis.DoesWebsiteExist(guid) + require.Nil(t, err, "failed to determine website existence") + assert.True(t, websiteExists, fmt.Sprintf("website '%s' does not exist", guid)) + + _, _, err = nomadClient.Jobs().Deregister(*job.ID, false, nil) + require.Nil(t, err, "failed to stop job") + + err = waitForJobStatus(job, "dead", 30*time.Second) + require.Nil(t, err, err) + + time.Sleep(5 * time.Second) + websiteExists, err = iis.DoesWebsiteExist(guid) + require.Nil(t, err, "failed to determine website existence") + assert.False(t, websiteExists) +} diff --git a/test/e2e/main_test.go b/test/e2e/main_test.go new file mode 100644 index 0000000..65708a0 --- /dev/null +++ b/test/e2e/main_test.go @@ -0,0 +1,192 @@ +package e2e + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/hashicorp/nomad/api" + nomad "github.com/hashicorp/nomad/api" + "github.com/roblox/nomad-driver-iis/iis" +) + +var ( + nomadClient *nomad.Client + testJobDir string +) + +func TestMain(m *testing.M) { + var err error + nomadClient, err = initNomad() + + if err != nil { + fmt.Println("Failed to initialize nomad client:", err) + os.Exit(1) + } + + // Wait for the nomad client to come online. + // Automation may perform a restart on the client and it takes a few secs to boot + isNomadHealthy := waitForNomadUp() + if !isNomadHealthy { + fmt.Println("Nomad failed to become healthy prior to tests") + os.Exit(1) + } + + // Get parent dir of working dir to help determine the nomad job dir + wd, err := os.Getwd() + if err != nil { + fmt.Println("Failed to get e2e parent dir: ", err) + os.Exit(1) + } + testJobDir = filepath.Join(filepath.Dir(wd), "jobs") + + os.Exit(m.Run()) +} + +func initNomad() (*nomad.Client, error) { + conf := nomad.DefaultConfig() + conf.Address = "http://localhost:4646" + + return nomad.NewClient(conf) +} + +func waitForNomadUp() bool { + timeout := 15 * time.Second + endTime := time.Now().Add(timeout) + isHealthy := false + for { + health, err := nomadClient.Agent().Health() + if err == nil { + // silently continue here as we will get errs while nomad comes back online + isHealthy = health.Server.Ok && health.Client.Ok + } + + if isHealthy || time.Now().After(endTime) { + break + } + time.Sleep(1 * time.Second) + } + + return isHealthy +} + +func cleanup() { + // Purge any existing Nomad jobs + nomadJobs, _, err := nomadClient.Jobs().List(nil) + if err != nil { + panic(fmt.Sprintf("Error getting jobs: %v", err)) + } + + for _, nomadJob := range nomadJobs { + nomadClient.Jobs().Deregister(nomadJob.ID, true, nil) + } + + // Clean IIS + if err = iis.PurgeIIS(); err != nil { + panic(fmt.Sprintf("Error purging IIS: %v", err)) + } + if err = iis.StartIIS(); err != nil { + panic(fmt.Sprintf("Error starting IIS: %v", err)) + } +} + +func waitForJobStatus(job *api.Job, status string, timeout time.Duration) error { + statusMatch := false + endTime := time.Now().Add(timeout) + for { + time.Sleep(1 * time.Second) + + jobInfo, _, err := nomadClient.Jobs().Info(*job.ID, nil) + if err != nil { + return fmt.Errorf("failed to get nomad job info: %v", err) + } + + statusMatch = strings.EqualFold(*jobInfo.Status, status) + + if statusMatch || time.Now().After(endTime) { + break + } + } + if !statusMatch { + return fmt.Errorf("nomad job failed to enter '%s' status in a timely manner", status) + } + + return nil +} + +func waitForAllocStatus(job *api.Job, status string, timeout time.Duration) error { + statusMatch := false + endTime := time.Now().Add(timeout) + for { + time.Sleep(1 * time.Second) + + allocs, _, err := nomadClient.Jobs().Allocations(*job.ID, false, nil) + if err != nil { + fmt.Errorf("error trying to gather job allocs: %v", err) + } + // alloc, _, err := nomadClient.Allocations().Info(allocs[0].ID, nil) + // require.Nil(t, err, "failed to get alloc info for first allocation found") + + if len(allocs) > 0 { + statusMatch = strings.EqualFold(allocs[0].ClientStatus, status) + } + + if statusMatch || time.Now().After(endTime) { + break + } + } + if !statusMatch { + return fmt.Errorf("nomad job failed to enter '%s' status in a timely manner", status) + } + + return nil +} + +func runJob(jobFilename string) (*api.Job, error) { + // Read Job + data, err := ioutil.ReadFile(filepath.Join(testJobDir, jobFilename)) + if err != nil { + return nil, fmt.Errorf("error trying to read nomad job spec: %v", err) + } + + // Parse Job + job, err := nomadClient.Jobs().ParseHCL(string(data), false) + if err != nil { + return job, fmt.Errorf("error trying to parse nomad job spec: %v", err) + } + + // Create test job + _, _, err = nomadClient.Jobs().Register(job, nil) + if err != nil { + return job, fmt.Errorf("error trying to register job: %v", err) + } + return job, err +} + +func runTestJobWithAllocStatus(jobFilename string, desiredStatus string, duration time.Duration) (*api.Job, error) { + job, err := runJob(jobFilename) + if err != nil { + return job, err + } + + // Wait for job to enter running status + err = waitForAllocStatus(job, desiredStatus, duration) + + return job, err +} + +func runTestJob(jobFilename string, desiredStatus string, duration time.Duration) (*api.Job, error) { + job, err := runJob(jobFilename) + if err != nil { + return job, err + } + + // Wait for job to enter running status + err = waitForJobStatus(job, desiredStatus, duration) + + return job, err +} diff --git a/test/jobs/bad-binding.nomad b/test/jobs/bad-binding.nomad new file mode 100644 index 0000000..e4ff95e --- /dev/null +++ b/test/jobs/bad-binding.nomad @@ -0,0 +1,35 @@ +job "bad-binding" { + datacenters = ["dc1"] + type = "service" + + group "iis-test" { + count = 1 + + network { + port "httplabel" { + static = 81 + } + } + + restart { + attempts = 0 + } + + task "iis-test" { + driver = "win_iis" + + config { + path = "C:\\inetpub\\wwwroot" + bindings { + type = "http" + port = "hzzplabel" + } + } + + resources { + cpu = 100 + memory = 20 + } + } + } +} diff --git a/test/jobs/bad-cert.nomad b/test/jobs/bad-cert.nomad new file mode 100644 index 0000000..3dd7216 --- /dev/null +++ b/test/jobs/bad-cert.nomad @@ -0,0 +1,36 @@ +job "bad-cert" { + datacenters = ["dc1"] + type = "service" + + group "iis-test" { + count = 1 + + network { + port "httplabel" { + static = 81 + } + } + + restart { + attempts = 0 + } + + task "iis-test" { + driver = "win_iis" + + config { + path = "C:\\inetpub\\wwwroot" + bindings { + type = "https" + port = "httplabel" + cert_hash = "NOT_VALID" + } + } + + resources { + cpu = 100 + memory = 20 + } + } + } +} diff --git a/test/jobs/iis-test.nomad b/test/jobs/iis-test.nomad new file mode 100644 index 0000000..68e00cd --- /dev/null +++ b/test/jobs/iis-test.nomad @@ -0,0 +1,115 @@ +job "iis-test" { + datacenters = ["dc1"] + type = "service" + + group "iis-test" { + count = 1 + + network { + port "httplabel" { + static = 81 + } + port "httpslabel" {} + port "httpslabel2" {} + } + + restart { + attempts = 0 + } + + task "iis-test" { + driver = "win_iis" + + config { + path = "local/website" + bindings { + type = "http" + port = "httplabel" + } + bindings { + type = "https" + port = "httpslabel" + cert_hash = "854d57551e79656159a0081054fbc08c6c648f86" + } + bindings { + type = "https" + port = "httpslabel2" + cert_name = "WMSVC-SHA2" + } + } + + + template { + data = < + + + TestEnvVar:<%=System.Environment.GetEnvironmentVariable("TEST_WIN_IIS") %> + + +EOH + } + + template { + destination = "local/website/cpu.aspx" + data = < + + + Untitled Page + + +
+ <%var input = Request.QueryString["cpu"]; + if (input == null) + { + input = "50"; + } + + int percentage; + if (!int.TryParse(input, out percentage) || percentage < 0 || percentage > 100) + throw new ArgumentException("percentage"); + + var watch = new System.Diagnostics.Stopwatch(); + watch.Start(); + while (true) + { + // Make the loop go on for "percentage" milliseconds then sleep the + // remaining percentage milliseconds. So 40% utilization means work 40ms and sleep 60ms + if (watch.ElapsedMilliseconds > percentage) + { + System.Threading.Thread.Sleep(100 - percentage); + watch.Reset(); + watch.Start(); + } + } + %> +
+ + + + + + + + +EOH + } + } + } +} diff --git a/test/jobs/user-test.nomad b/test/jobs/user-test.nomad new file mode 100644 index 0000000..02fa382 --- /dev/null +++ b/test/jobs/user-test.nomad @@ -0,0 +1,44 @@ +job "user-test" { + datacenters = ["dc1"] + type = "service" + + group "iis-test" { + count = 1 + + network { + port "httplabel" { + static = 81 + } + } + + restart { + attempts = 0 + } + + task "iis-test" { + driver = "win_iis" + + config { + path = "C:\\inetpub\\wwwroot" + bindings { + type = "http" + port = "httplabel" + } + } + + template { + destination = "secrets/file.env" + env = true + data = <