Repository: m3db/m3db-operator
Pull request #245 (2020-10-27 14:56:16)

Add calls to /namespace/ready if supported by coordinator
Add support for transitioning namespaces to a ready state after they've been created. This operates as a signal to coordinators pulling namespace information from etcd that the namespace is ready for use.

codecov[bot]: # [Codecov](https://codecov.io/gh/m3db/m3db-operator/pull/245?src=pr&el=h1) Report
> Merging [#245](https://codecov.io/gh/m3db/m3db-operator/pull/245?src=pr&el=desc) into [master](https://codecov.io/gh/m3db/m3db-operator/commit/f396e2b86e3625c40b67e943dbf2b76f2e68e335?el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `77.08%`.


[![Impacted file tree graph](https://codecov.io/gh/m3db/m3db-operator/pull/245/graphs/tree.svg?width=650&height=150&src=pr&token=jtHDyWhsto)](https://codecov.io/gh/m3db/m3db-operator/pull/245?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #245      +/-   ##
==========================================
- Coverage   76.29%   76.27%   -0.02%     
==========================================
  Files          30       30              
  Lines        2168     2213      +45     
==========================================
+ Hits         1654     1688      +34     
- Misses        380      388       +8     
- Partials      134      137       +3     
```


------

[Continue to review full report at Codecov](https://codecov.io/gh/m3db/m3db-operator/pull/245?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/m3db/m3db-operator/pull/245?src=pr&el=footer). Last update [f396e2b...af0a1b5](https://codecov.io/gh/m3db/m3db-operator/pull/245?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/go.mod b/go.mod
index ccaa272..f33c1d2 100644
--- a/go.mod
+++ b/go.mod
@@ -26,9 +26,8 @@ require (
 	github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect
 	github.com/googleapis/gnostic v0.3.1 // indirect
 	github.com/hashicorp/go-retryablehttp v0.6.0
-	github.com/imdario/mergo v0.3.7 // indirect
 	github.com/m3db/build-tools v0.0.0-20181013000606-edd1bdd1df8a
-	github.com/m3db/m3 v0.15.16-0.20200928031244-a7b499d3884d
+	github.com/m3db/m3 v0.15.18-0.20201027011129-53414ba8082a
 	github.com/m3db/m3x v0.0.0-20190408051622-ebf3c7b94afd
 	github.com/m3db/tools v0.0.0-20181008195521-c6ded3f34878
 	github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d // indirect
diff --git a/go.sum b/go.sum
index 35f209c..acd86ea 100644
--- a/go.sum
+++ b/go.sum
@@ -40,6 +40,7 @@ github.com/DataDog/datadog-go v3.7.1+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
 github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
 github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/MichaelTJones/pcg v0.0.0-20180122055547-df440c6ed7ed h1:hQC4FSwvsLH6rOLJTndsHnANARF9RwW4PbrDTjks/0A=
 github.com/MichaelTJones/pcg v0.0.0-20180122055547-df440c6ed7ed/go.mod h1:NQ4UMHqyfXyYVmZopcfwPRWJa0rw2aH16eDIltReVUo=
 github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
@@ -164,6 +165,9 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/daviddengcn/go-algs v0.0.0-20180330170136-fe23fabd9d06/go.mod h1:CpyLopUWBmqupyWU6OlSfrzgIuzNq0R6DXzM74O9RMs=
+github.com/daviddengcn/go-assert v0.0.0-20150305222929-ba7e68aeeff6/go.mod h1:N+OekMaElW3rSAfDdNX6Dff3HS237/OhC08jYFW4oCw=
+github.com/daviddengcn/go-villa v0.0.0-20160111144444-3f35da8ba982/go.mod h1:U8xNoHcXfPnZzy9zCxeKRjaJgC1d3613rFHjZVVAqKc=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
@@ -405,6 +409,10 @@ github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1
 github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
 github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
 github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
+github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
+github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
+github.com/golangplus/sort v0.0.0-20160821213012-8253da0d33c1/go.mod h1:QfPzxzvnnTTxeT9BlGNopDfHRXm1ITdu9ZBkwK1X7eA=
+github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -522,8 +530,8 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK
 github.com/hydrogen18/stalecucumber v0.0.0-20151102144322-9b38526d4bdf/go.mod h1:KE5xQoh/IqNckSFoQXL5o5nEkrBiUDxatgac7TSMQ8Y=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
-github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
+github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8=
 github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
@@ -540,6 +548,7 @@ github.com/jcmturner/gokrb5/v8 v8.2.0/go.mod h1:T1hnNppQsBtxW0tCHMHTkAt8n/sABdzZ
 github.com/jcmturner/rpc/v2 v2.0.2/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
 github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8=
 github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@@ -582,7 +591,9 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
 github.com/leanovate/gopter v0.2.8/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8=
 github.com/lib/pq v1.6.0/go.mod h1:4vXEAYvW1fRQ2/FhZ78H73A60MHw1geSm145z2mdY1g=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo=
 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.18.1 h1:vi1F1IQ8N7hNWytK9DpJsUfQhGuNSc19z330K6vl4zk=
 github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
 github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
 github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
@@ -591,8 +602,8 @@ github.com/m3db/bloom v3.0.1+incompatible/go.mod h1:W6XzpFw4t+CIYq+NGyp5c2394YsU
 github.com/m3db/bloom/v4 v4.0.0-20200901140942-52efb8544fe9/go.mod h1:JDmGHlO6ygyY1V9eOHtXiNl3+axznDTrBqwWEeWALlQ=
 github.com/m3db/build-tools v0.0.0-20181013000606-edd1bdd1df8a h1:CwsSHIJLeCESKdZ844jXg/3rQD3yA5azuVlJBp5w8U8=
 github.com/m3db/build-tools v0.0.0-20181013000606-edd1bdd1df8a/go.mod h1:Pk9AtZeKuCO2xcAth0gxwzRNFv4lV26GPSx4I6A7DQ8=
-github.com/m3db/m3 v0.15.16-0.20200928031244-a7b499d3884d h1:TiVYCb0Mh+VsMnRJCF6LPDbHvMIUrfmaqu9qlbIx27k=
-github.com/m3db/m3 v0.15.16-0.20200928031244-a7b499d3884d/go.mod h1:AqwPdgeCciBDvCtHRcAInN9RIjLsJSirntAl6b3bDOU=
+github.com/m3db/m3 v0.15.18-0.20201027011129-53414ba8082a h1:y/TmkQhLawVSdvfU4bcHQUeNxGkuiPzqdfYIz4QPJCI=
+github.com/m3db/m3 v0.15.18-0.20201027011129-53414ba8082a/go.mod h1:NpS2lL+KcoAtHoAhfOKcEn4g3ih7NCKGGMUl0sEMyQg=
 github.com/m3db/m3x v0.0.0-20190408051622-ebf3c7b94afd h1:wzLBtXzxZM9b6IXwLSRE5crynocLTCuRDpGDaOJzyuI=
 github.com/m3db/m3x v0.0.0-20190408051622-ebf3c7b94afd/go.mod h1:zLbcVb352e3Jsg62A6zzEhZ1gumeFsiamTqDs9ZmZrs=
 github.com/m3db/prometheus_client_golang v0.8.1 h1:t7w/tcFws81JL1j5sqmpqcOyQOpH4RDOmIe3A3fdN3w=
@@ -822,8 +833,10 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo
 github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
 github.com/satori/go.uuid v0.0.0-20160603004225-b111a074d5ef/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/seborama/govcr v2.2.1+incompatible/go.mod h1:EgcISudCCYDLzbiAImJ8i7kk4+wTA44Kp+j4S0LhASI=
 github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
@@ -931,7 +944,9 @@ github.com/twmb/murmur3 v1.1.4/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq
 github.com/twotwotwo/sorts v0.0.0-20160814051341-bf5c1f2b8553/go.mod h1:Rj7Csq/tZ/egz+Ltc2IVpsA5309AmSMEswjkTZmq2Xc=
 github.com/uber-go/tally v3.3.13+incompatible h1:5ic2UsDwjcWsw9jvEdWEE2XsmGCLMTt5Ukg4d74fed4=
 github.com/uber-go/tally v3.3.13+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU=
+github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U=
 github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw=
 github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
 github.com/uber/tchannel-go v1.14.0/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@@ -950,6 +965,7 @@ github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOV
 github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
 github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
 github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
+github.com/wjdp/htmltest v0.13.0/go.mod h1:XUQ9x7nIXeiOrdzAnB3nDGGZcdv0D3lqqlZsOusadRE=
 github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
 github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
@@ -1081,6 +1097,7 @@ golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
 golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1137,6 +1154,7 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1250,6 +1268,7 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
 google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
 google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U=
 google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
@@ -1266,6 +1285,7 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
 google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
 gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@@ -1298,7 +1318,7 @@ gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuv
 gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
 gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/russross/blackfriday.v2 v2.0.0/go.mod h1:6sSBNz/GtOm/pJTuh5UmBK2ZHfmnxGbl2NZg1UliSOI=
+gopkg.in/seborama/govcr.v2 v2.4.2/go.mod h1:0ERlmX5DdmlKL6gvWYyegw24WXF8lr97+AM+hA8i2Bo=
 gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
 gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
diff --git a/pkg/controller/m3admin_client.go b/pkg/controller/m3admin_client.go
index 0f05218..4bbed62 100644
--- a/pkg/controller/m3admin_client.go
+++ b/pkg/controller/m3admin_client.go
@@ -237,6 +237,10 @@ func (c errorNamespaceClient) Delete(namespace string) error {
 	return c.err
 }
 
+func (c errorNamespaceClient) Ready(_ *admin.NamespaceReadyRequest) error {
+	return c.err
+}
+
 // errorPlacementClient follows the same pattern of errorNamespaceClient for
 // placement.Client.
 type errorPlacementClient struct {
diff --git a/pkg/controller/update_cluster.go b/pkg/controller/update_cluster.go
index 91d99dd..730b38e 100644
--- a/pkg/controller/update_cluster.go
+++ b/pkg/controller/update_cluster.go
@@ -73,7 +73,11 @@ func (c *M3DBController) reconcileNamespaces(cluster *myspec.M3DBCluster) error
 		return err
 	}
 
-	return c.createNamespaces(cluster, resp.Registry)
+	if err := c.createNamespaces(cluster, resp.Registry); err != nil {
+		return err
+	}
+
+	return c.readyNamespaces(cluster, resp.Registry)
 }
 
 // createNamespaces will attempt to create in the cluster all namespaces which
@@ -105,6 +109,43 @@ func (c *M3DBController) createNamespaces(cluster *myspec.M3DBCluster, registry
 	return nil
 }
 
+// readyNamespaces will attempt to mark all namespaces in the initializing state as ready.
+func (c *M3DBController) readyNamespaces(cluster *myspec.M3DBCluster, registry *dbns.Registry) error {
+	// NB(nate): Marking namespaces ready (without force) involves checking dbnodes for namespace
+	// availability. External coordinators do not have connections to dbnodes so don't attempt this
+	// step for now.
+	if cluster.Spec.ExternalCoordinator != nil {
+		c.logger.Debug("using an external coordinator. will not attempt to ready namespaces")
+		return nil
+	}
+
+	toReady := namespacesToReady(registry, cluster.Spec.Namespaces)
+	for _, ns := range toReady {
+		req := &admin.NamespaceReadyRequest{
+			Name: ns.Name,
+		}
+		err := c.adminClient.namespaceClientForCluster(cluster).Ready(req)
+		cause := pkgerrors.Cause(err)
+		// NB(nate): Due to bug in coordinator API routing logic, missing routes may
+		// also be returned as 405s (i.e. method not allowed). So check for that in addition
+		// to 404s.
+		if cause == m3admin.ErrNotFound || cause == m3admin.ErrMethodNotAllowed {
+			c.logger.Info("coordinator does not yet support the ready endpoint. " +
+				"skipping readying namespaces until upgraded")
+			return nil
+		}
+		if err != nil {
+			c.logger.Error("error readying namespace",
+				zap.String("namespace", ns.Name),
+				zap.Error(err))
+
+			return fmt.Errorf("error readying namespace '%s': %v", ns.Name, err)
+		}
+	}
+
+	return nil
+}
+
 // pruneNamespaces will delete any namespaces in the m3db cluster that aren't
 // in the spec.
 func (c *M3DBController) pruneNamespaces(cluster *myspec.M3DBCluster, registry *dbns.Registry) error {
@@ -116,6 +157,8 @@ func (c *M3DBController) pruneNamespaces(cluster *myspec.M3DBCluster, registry *
 			c.recorder.NormalEvent(cluster, eventer.ReasonDeleting, "deleted namespace "+ns)
 			continue
 		}
+		// TODO(nate): Set the StagingStatus to initializing here once we're guaranteed that each
+		// coordinator can support receiving the JSON field.
 
 		if pkgerrors.Cause(err) == m3admin.ErrNotFound {
 			c.logger.Info("namespace has already been deleted", zap.String("namespace", ns))
@@ -161,6 +204,30 @@ func namespacesToDelete(registry *dbns.Registry, specNs []myspec.Namespace) (toD
 	return
 }
 
+// namespacesToReady returns an array of namespaces that are in the initializing state.
+func namespacesToReady(registry *dbns.Registry, specNs []myspec.Namespace) (toReady []myspec.Namespace) {
+	namespacesToReady := make(map[string]myspec.Namespace)
+	// Add namespaces we've just created.
+	for _, ns := range specNs {
+		if _, ok := registry.Namespaces[ns.Name]; !ok {
+			namespacesToReady[ns.Name] = ns
+		}
+	}
+
+	// Add any existing namespaces we've found not in the ready state.
+	for name, options := range registry.Namespaces {
+		if options.StagingState != nil && options.StagingState.Status != dbns.StagingStatus_READY {
+			namespacesToReady[name] = myspec.Namespace{Name: name}
+		}
+	}
+
+	for _, ns := range namespacesToReady {
+		toReady = append(toReady, ns)
+	}
+
+	return
+}
+
 func (c *M3DBController) validatePlacementWithStatus(cluster *myspec.M3DBCluster) (*myspec.M3DBCluster, error) {
 	plClient := c.adminClient.placementClientForCluster(cluster)
 	_, err := plClient.Get()
diff --git a/pkg/m3admin/client.go b/pkg/m3admin/client.go
index 6660eeb..faf5614 100644
--- a/pkg/m3admin/client.go
+++ b/pkg/m3admin/client.go
@@ -46,6 +46,9 @@ var (
 
 	// ErrNotFound indicates that HTTP status was not found
 	ErrNotFound = errors.New("status not found")
+
+	// ErrMethodNotAllowed indicates that HTTP status was method not allowed
+	ErrMethodNotAllowed = errors.New("method not allowed")
 )
 
 // Client is an m3admin client.
@@ -176,6 +179,10 @@ func (c *client) DoHTTPRequest(
 		return nil, pkgerrors.WithMessage(ErrNotFound, errMsg)
 	}
 
+	if response.StatusCode == http.StatusMethodNotAllowed {
+		return nil, pkgerrors.WithMessage(ErrMethodNotAllowed, errMsg)
+	}
+
 	return nil, pkgerrors.WithMessage(ErrNotOk, errMsg)
 }
 
diff --git a/pkg/m3admin/namespace/client.go b/pkg/m3admin/namespace/client.go
index bdfa995..accc0a1 100644
--- a/pkg/m3admin/namespace/client.go
+++ b/pkg/m3admin/namespace/client.go
@@ -34,8 +34,10 @@ import (
 )
 
 const (
-	namespaceBaseURL   = "/api/v1/namespace"
-	namespaceDeleteFmt = namespaceBaseURL + "/%s"
+	namespaceLegacyBaseURL = "/api/v1/namespace"
+	namespaceBaseURL       = "/api/v1/services/m3db/namespace"
+	namespaceDeleteFmt     = namespaceLegacyBaseURL + "/%s"
+	namespaceReadyURL      = namespaceBaseURL + "/ready"
 )
 
 type namespaceClient struct {
@@ -100,7 +102,7 @@ func NewClient(opts ...Option) (Client, error) {
 
 // Create will create a namespace
 func (n *namespaceClient) Create(req *admin.NamespaceAddRequest) error {
-	url := n.url + namespaceBaseURL
+	url := n.url + namespaceLegacyBaseURL
 	err := n.client.DoHTTPJSONPBRequest(http.MethodPost, url, req, nil)
 	if err != nil {
 		return err
@@ -112,7 +114,7 @@ func (n *namespaceClient) Create(req *admin.NamespaceAddRequest) error {
 // List will retrieve all namespaces
 func (n *namespaceClient) List() (*admin.NamespaceGetResponse, error) {
 	var (
-		url  = n.url + namespaceBaseURL
+		url  = n.url + namespaceLegacyBaseURL
 		resp = &admin.NamespaceGetResponse{}
 	)
 	err := n.client.DoHTTPJSONPBRequest(http.MethodGet, url, nil, resp)
@@ -139,3 +141,17 @@ func (n *namespaceClient) Delete(namespace string) error {
 	n.logger.Info("successfully deleted namespace")
 	return nil
 }
+
+// Ready will mark a namespace as ready
+func (n *namespaceClient) Ready(req *admin.NamespaceReadyRequest) error {
+	var (
+		url  = n.url + namespaceReadyURL
+		resp = &admin.NamespaceReadyResponse{}
+	)
+	err := n.client.DoHTTPJSONPBRequest(http.MethodPost, url, req, resp)
+	if err != nil {
+		return err
+	}
+	n.logger.Info("marked namespace as ready", zap.String("namespace", req.Name))
+	return nil
+}
diff --git a/pkg/m3admin/namespace/client_mock.go b/pkg/m3admin/namespace/client_mock.go
index 518584b..3c5e206 100644
--- a/pkg/m3admin/namespace/client_mock.go
+++ b/pkg/m3admin/namespace/client_mock.go
@@ -97,3 +97,17 @@ func (mr *MockClientMockRecorder) Delete(namespace interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), namespace)
 }
+
+// Ready mocks base method
+func (m *MockClient) Ready(request *admin.NamespaceReadyRequest) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Ready", request)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Ready indicates an expected call of Ready
+func (mr *MockClientMockRecorder) Ready(request interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ready", reflect.TypeOf((*MockClient)(nil).Ready), request)
+}
diff --git a/pkg/m3admin/namespace/types.go b/pkg/m3admin/namespace/types.go
index f2b7e14..f84607e 100644
--- a/pkg/m3admin/namespace/types.go
+++ b/pkg/m3admin/namespace/types.go
@@ -33,4 +33,6 @@ type Client interface {
 	List() (*admin.NamespaceGetResponse, error)
 	// Delete will delete a namespace given a name
 	Delete(namespace string) error
+	// Ready will attempt to mark a namespace as ready.
+	Ready(request *admin.NamespaceReadyRequest) error
 }


Test patch:
diff --git a/pkg/controller/update_cluster_test.go b/pkg/controller/update_cluster_test.go
index a6e877f..535fb5e 100644
--- a/pkg/controller/update_cluster_test.go
+++ b/pkg/controller/update_cluster_test.go
@@ -61,7 +61,17 @@ type namespaceMatcher struct {
 }
 
 func (n namespaceMatcher) Matches(x interface{}) bool {
-	return x.(*admin.NamespaceAddRequest).Name == n.name
+	addReq, ok := x.(*admin.NamespaceAddRequest)
+	if ok {
+		return addReq.Name == n.name
+	}
+
+	readyReq, ok := x.(*admin.NamespaceReadyRequest)
+	if ok {
+		return readyReq.Name == n.name
+	}
+
+	return false
 }
 
 func (namespaceMatcher) String() string {
@@ -89,6 +99,7 @@ func TestReconcileNamespaces(t *testing.T) {
 
 	nsMock.EXPECT().Delete("a").Return(nil)
 	nsMock.EXPECT().Create(namespaceMatcher{"metrics-10s:2d"}).Return(nil)
+	nsMock.EXPECT().Ready(namespaceMatcher{"metrics-10s:2d"}).Return(nil)
 
 	err := controller.reconcileNamespaces(cluster)
 	assert.NoError(t, err)
@@ -151,6 +162,70 @@ func TestCreateNamespaces(t *testing.T) {
 	assert.NoError(t, err)
 }
 
+func TestReadyNamespaces(t *testing.T) {
+	cluster := getFixture("cluster-simple.yaml", t)
+	cluster.Spec.Namespaces = append(cluster.Spec.Namespaces, myspec.Namespace{
+		Name:   "foo",
+		Preset: "10s:2d",
+	})
+
+	deps := newTestDeps(t, &testOpts{
+		crdObjects: []runtime.Object{cluster},
+	})
+	nsMock := deps.namespaceClient
+	controller := deps.newController(t)
+	defer deps.cleanup()
+
+	registry := &dbns.Registry{Namespaces: map[string]*dbns.NamespaceOptions{}}
+
+	nsMock.EXPECT().Ready(namespaceMatcher{"metrics-10s:2d"}).Return(nil)
+	nsMock.EXPECT().Ready(namespaceMatcher{"foo"}).Return(nil)
+
+	err := controller.readyNamespaces(cluster, registry)
+	assert.NoError(t, err)
+}
+
+func TestReadyNamespacesNotSupported(t *testing.T) {
+	cluster := getFixture("cluster-simple.yaml", t)
+	cluster.Spec.ExternalCoordinator = &myspec.ExternalCoordinatorConfig{}
+
+	deps := newTestDeps(t, &testOpts{
+		crdObjects: []runtime.Object{cluster},
+	})
+	nsMock := deps.namespaceClient
+	controller := deps.newController(t)
+	defer deps.cleanup()
+
+	registry := &dbns.Registry{Namespaces: map[string]*dbns.NamespaceOptions{}}
+
+	nsMock.EXPECT().Ready(gomock.Any()).Times(0)
+
+	err := controller.readyNamespaces(cluster, registry)
+	assert.NoError(t, err)
+}
+
+func TestReadyNamespacesOldCoordinator(t *testing.T) {
+	cluster := getFixture("cluster-simple.yaml", t)
+	cluster.Spec.Namespaces = append(cluster.Spec.Namespaces, myspec.Namespace{
+		Name:   "foo",
+		Preset: "10s:2d",
+	})
+
+	deps := newTestDeps(t, &testOpts{
+		crdObjects: []runtime.Object{cluster},
+	})
+	nsMock := deps.namespaceClient
+	controller := deps.newController(t)
+	defer deps.cleanup()
+
+	registry := &dbns.Registry{Namespaces: map[string]*dbns.NamespaceOptions{}}
+
+	nsMock.EXPECT().Ready(namespaceMatcher{"metrics-10s:2d"}).Return(m3admin.ErrNotFound)
+
+	err := controller.readyNamespaces(cluster, registry)
+	assert.NoError(t, err)
+}
+
 func TestNamespacesToCreate(t *testing.T) {
 	tests := []struct {
 		registry   *dbns.Registry
@@ -235,6 +310,58 @@ func TestNamespacesToDelete(t *testing.T) {
 	}
 }
 
+func TestNamespacesToReady(t *testing.T) {
+	tests := []struct {
+		registry   *dbns.Registry
+		namespaces []myspec.Namespace
+		exp        []myspec.Namespace
+	}{
+		{
+			registry: &dbns.Registry{
+				Namespaces: map[string]*dbns.NamespaceOptions{
+					"foo": {},
+				},
+			},
+			namespaces: []myspec.Namespace{
+				{Name: "foo"},
+			},
+		},
+		{
+			registry: &dbns.Registry{
+				Namespaces: map[string]*dbns.NamespaceOptions{
+					"foo": {},
+				},
+			},
+			namespaces: []myspec.Namespace{
+				{Name: "foo"},
+				{Name: "baz"},
+			},
+			exp: []myspec.Namespace{
+				{Name: "baz"},
+			},
+		},
+		{
+			registry: &dbns.Registry{
+				Namespaces: map[string]*dbns.NamespaceOptions{
+					"foo": {StagingState: &dbns.StagingState{Status: dbns.StagingStatus_INITIALIZING}},
+				},
+			},
+			namespaces: []myspec.Namespace{
+				{Name: "baz"},
+			},
+			exp: []myspec.Namespace{
+				{Name: "baz"},
+				{Name: "foo"},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		res := namespacesToReady(test.registry, test.namespaces)
+		assert.Equal(t, test.exp, res)
+	}
+}
+
 func TestSetPodBootstrappingStatus(t *testing.T) {
 	cluster := getFixture("cluster-simple.yaml", t)
 	assert.False(t, cluster.Status.HasPodBootstrapping())
diff --git a/pkg/m3admin/client_test.go b/pkg/m3admin/client_test.go
index ed1cf94..215b020 100644
--- a/pkg/m3admin/client_test.go
+++ b/pkg/m3admin/client_test.go
@@ -148,6 +148,10 @@ func TestClient_DoHTTPRequest_Err(t *testing.T) {
 			code:   404,
 			expErr: ErrNotFound,
 		},
+		{
+			code:   405,
+			expErr: ErrMethodNotAllowed,
+		},
 		{
 			code:   500,
 			expErr: ErrNotOk,
diff --git a/pkg/m3admin/namespace/client_test.go b/pkg/m3admin/namespace/client_test.go
index 2b1a0ad..5eb45ce 100644
--- a/pkg/m3admin/namespace/client_test.go
+++ b/pkg/m3admin/namespace/client_test.go
@@ -158,3 +158,17 @@ func TestDeleteErr(t *testing.T) {
 	err := client.Delete("default")
 	require.NotNil(t, err)
 }
+
+func TestReady(t *testing.T) {
+	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(200)
+		w.Write([]byte(`{"ready":true}`))
+	}))
+	defer s.Close()
+	client := newNamespaceClient(t, s.URL)
+
+	err := client.Ready(&admin.NamespaceReadyRequest{
+		Name: "foo",
+	})
+	require.NoError(t, err)
+}