Repository: kubernetes-incubator/external-dns
Pull request #955 (2019-03-31 20:06:30)

Add Cloud Foundry routes as a source
This is a proposal to add Routes from Cloud Foundry as an optional source.

Delivers a ubiquitous Developer Experience to use External DNS open source project for both Kubernetes as well as Cloud Foundry. This streamlines application publishing using single, automated  DNS management across multiple application platforms and multiple clouds. Currently, this functionality does not exist across multiple Cloud Foundry foundations (or AZs).

General idea here is that you would point external-dns to a Cloud Foundry foundation/endpoint and it would map all routes under all domains back to the DNS zone you would like to manage with external-dns. This allows for routes registered in Cloud Foundry to have their DNS managed in an external provider (Route 53, Google Cloud DNS, etc).

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To fully approve this pull request, please assign additional approvers.
We suggest the following additional approver: **hjacobs**

If they are not already assigned, you can assign the PR to them by writing `/assign @hjacobs` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-incubator%2Fexternal-dns).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-incubator/external-dns/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["hjacobs"]} -->
njuettner: Thank you for your PR! 

You may want to fix formatting first:

pkg/apis/externaldns/types.go:186:33:warning: missing ',' before newline in composite literal (deadcode)
pkg/apis/externaldns/types.go:186:33:warning: missing ',' before newline in composite literal (golint)

Additionally to that you have to make sure `dep` has all your new importing packages otherwise the build will fail.

After fixing we'll have a look :slightly_smiling_face:.

dgrizzanti: Thanks for taking a look @njuettner. I've fixed those 2 issues but getting stuck on something else with the build. It seems like it's not finding the new config options I've added even though they have been added to `source/store.go`. Looking at the version of the project that is pulled into the vendor directory during the build?

Let me know if I'm missing something. Thanks!
Raffo: From the TravisCI output it looks like there is a missing method to implement in the test and other issues: 

```
# github.com/kubernetes-incubator/external-dns/source [github.com/kubernetes-incubator/external-dns/source.test]
source/route.go:41:31: rs.client.Config undefined (type *cfclient.Client has no field or method Config)
source/store_test.go:64:25: cannot use mockClientGenerator (type *MockClientGenerator) as type ClientGenerator in argument to ByNames:
	*MockClientGenerator does not implement ClientGenerator (missing CloudFoundryClient method)
source/store_test.go:73:25: cannot use mockClientGenerator (type *MockClientGenerator) as type ClientGenerator in argument to ByNames:
	*MockClientGenerator does not implement ClientGenerator (missing CloudFoundryClient method)
source/store_test.go:83:25: cannot use mockClientGenerator (type *MockClientGenerator) as type ClientGenerator in argument to ByNames:
	*MockClientGenerator does not implement ClientGenerator (missing CloudFoundryClient method)
source/store_test.go:92:19: cannot use mockClientGenerator (type *MockClientGenerator) as type ClientGenerator in argument to ByNames:
	*MockClientGenerator does not implement ClientGenerator (missing CloudFoundryClient method)
source/store_test.go:95:18: cannot use mockClientGenerator (type *MockClientGenerator) as type ClientGenerator in argument to ByNames:
	*MockClientGenerator does not implement ClientGenerator (missing CloudFoundryClient method)
source/store_test.go:98:18: cannot use mockClientGenerator (type *MockClientGenerator) as type ClientGenerator in argument to ByNames:
	*MockClientGenerator does not implement ClientGenerator (missing CloudFoundryClient method)
source/store_test.go:107:19: cannot use mockClientGenerator (type *MockClientGenerator) as type ClientGenerator in argument to ByNames:
	*MockClientGenerator does not implement ClientGenerator (missing CloudFoundryClient method)
FAILED   /home/travis/gopath/src/github.com/kubernetes-incubator/external-dns/vendor/github.com/kubernetes/repo-infra/verify/go-tools/verify-govet.sh	31s
The command "vendor/github.com/kubernetes/repo-infra/verify/verify-go-src.sh -v --rootdir $(pwd)" exited with 1.
```

Do you think you can work at fixing those issues? 
dgrizzanti: thanks @Raffo will work through these issues
dgrizzanti: Issues are fixed but still missing a few tests - will work on adding those
Raffo: @dgrizzanti is this ready for review? 
dgrizzanti: @Raffo yes - it should be all set. Only comment would be that I opened this PR more as a proposal for adding the feature. The code works, but I probably need to update the docs on how to use it/intention, etc. I wanted someone to at least give the 👍 on the approach prior putting together a full set of docs.

I'm fine with adding all of that after the fact in if you think I'm good but wanted to get through that checkpoint first.

Thanks!
njuettner: @dgrizzanti we switched to `go modules`, could you please rebase from master?
k8s-ci-robot: @dgrizzanti: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
dgrizzanti: @njuettner rebased and updated with go modules
k8s-ci-robot: @dgrizzanti: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

Patch:
diff --git a/go.mod b/go.mod
index 0172153c..abc39dfd 100644
--- a/go.mod
+++ b/go.mod
@@ -14,6 +14,7 @@ require (
 	github.com/aws/aws-sdk-go v1.13.32
 	github.com/cenkalti/backoff v2.1.1+incompatible // indirect
 	github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730
+	github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
 	github.com/coreos/bbolt v1.3.2 // indirect
 	github.com/coreos/etcd v3.3.10+incompatible
 	github.com/coreos/go-semver v0.2.0 // indirect
@@ -31,6 +32,7 @@ require (
 	github.com/go-resty/resty v1.8.0 // indirect
 	github.com/gogo/googleapis v1.1.0 // indirect
 	github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
+	github.com/golang/mock v1.2.0 // indirect
 	github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a // indirect
 	github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect
 	github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
@@ -63,7 +65,7 @@ require (
 	github.com/onsi/gomega v1.5.0 // indirect
 	github.com/oracle/oci-go-sdk v1.8.0
 	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
-	github.com/pkg/errors v0.8.0
+	github.com/pkg/errors v0.8.1
 	github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829
 	github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0
 	github.com/satori/go.uuid v1.2.0 // indirect
diff --git a/go.sum b/go.sum
index 2389ca51..a2ee9434 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,8 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f h1:UrKzEwTgeiff9vxdrfdqxibzpWjxLnuXDI5m6z3GJAk=
+code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI=
 git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
 github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible h1:FhnlL7/4O3gAB7EBgN43vA3Bb0fAlCBIMm9avXbcHlE=
 github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
@@ -8,6 +10,8 @@ github.com/Azure/go-autorest v10.9.0+incompatible h1:3ccqKLQg+scl0J6krcDgih2Rl+G
 github.com/Azure/go-autorest v10.9.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
+github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
@@ -34,7 +38,10 @@ github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730 h1:+TK6ytATp7coqI4UlTBboFYD0kSkWZt6L6/T+1yBK6k=
 github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730/go.mod h1:qKQ9S///VKEax9N8kFel9/AvmnkYgvb8uiKTnoVFvpg=
+github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s=
+github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14=
 github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
+github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
 github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
@@ -74,6 +81,7 @@ github.com/go-ini/ini v1.32.0 h1:/MArBHSS0TFR28yPPDK1vPIjt4wUnPBfb81i6iiyKvA=
 github.com/go-ini/ini v1.32.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
 github.com/go-resty/resty v1.8.0 h1:vbNCxbHOWCototzwxf3L63PQCKx6xgT6v8SHfoqkp6U=
 github.com/go-resty/resty v1.8.0/go.mod h1:n37daLLGIHq2FFYHxg+FYQiwA95FpfNI+A9uxoIYGRk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -102,6 +110,7 @@ github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhp
 github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4=
 github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
+github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
@@ -139,6 +148,7 @@ github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@@ -159,6 +169,7 @@ github.com/linode/linodego v0.3.0 h1:I83pEPg4owSy5pCPaKix7xkGbWIjPxmAoc/Yu5OYDDY
 github.com/linode/linodego v0.3.0/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY=
 github.com/lyft/protoc-gen-validate v0.0.14 h1:xbdDVIHd0Xq5Bfzu+8JR9s7mFmJPMvNLmfGhgcHJdFU=
 github.com/lyft/protoc-gen-validate v0.0.14/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
+github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI=
 github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
 github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@@ -191,11 +202,14 @@ github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm
 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 github.com/oracle/oci-go-sdk v1.8.0 h1:4SO45bKV0I3/Mn1os3ANDZmV0eSE5z5CLdSUIkxtyzs=
 github.com/oracle/oci-go-sdk v1.8.0/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
+github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0=
 github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
 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/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.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=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -219,9 +233,9 @@ 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/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180725160413-e900ae048470/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
+github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
 github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss=
 github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -282,6 +296,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJV
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/main.go b/main.go
index fe8c5bb8..d707b645 100644
--- a/main.go
+++ b/main.go
@@ -82,6 +82,9 @@ func main() {
 		KubeMaster:                  cfg.Master,
 		ServiceTypeFilter:           cfg.ServiceTypeFilter,
 		IstioIngressGatewayServices: cfg.IstioIngressGatewayServices,
+		CFAPIEndpoint:               cfg.CFAPIEndpoint,
+		CFUsername:                  cfg.CFUsername,
+		CFPassword:                  cfg.CFPassword,
 	}
 
 	// Lookup all the selected sources by names and pass them the desired configuration.
diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go
index 1050d67a..0b3c4dde 100644
--- a/pkg/apis/externaldns/types.go
+++ b/pkg/apis/externaldns/types.go
@@ -105,6 +105,9 @@ type Config struct {
 	CRDSourceAPIVersion         string
 	CRDSourceKind               string
 	ServiceTypeFilter           []string
+	CFAPIEndpoint               string
+	CFUsername                  string
+	CFPassword                  string
 	RFC2136Host                 string
 	RFC2136Port                 int
 	RFC2136Zone                 string
@@ -182,6 +185,9 @@ var defaultConfig = &Config{
 	CRDSourceAPIVersion:         "externaldns.k8s.io/v1alpha1",
 	CRDSourceKind:               "DNSEndpoint",
 	ServiceTypeFilter:           []string{},
+	CFAPIEndpoint:               "",
+	CFUsername:                  "",
+	CFPassword:                  "",
 	RFC2136Host:                 "",
 	RFC2136Port:                 0,
 	RFC2136Zone:                 "",
@@ -245,8 +251,13 @@ func (cfg *Config) ParseFlags(args []string) error {
 	// Flags related to Istio
 	app.Flag("istio-ingress-gateway", "The fully-qualified name of the Istio ingress gateway service. Flag can be specified multiple times (default: istio-system/istio-ingressgateway)").Default("istio-system/istio-ingressgateway").StringsVar(&cfg.IstioIngressGatewayServices)
 
+	// Flags related to cloud foundry
+	app.Flag("cf-api-endpoint", "The fully-qualified domain name of the cloud foundry instance you are targeting").Default(defaultConfig.CFAPIEndpoint).StringVar(&cfg.CFAPIEndpoint)
+	app.Flag("cf-username", "The username to log into the cloud foundry API").Default(defaultConfig.CFUsername).StringVar(&cfg.CFUsername)
+	app.Flag("cf-password", "The password to log into the cloud foundry API").Default(defaultConfig.CFPassword).StringVar(&cfg.CFPassword)
+
 	// Flags related to processing sources
-	app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, fake, connector, istio-gateway, crd").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "istio-gateway", "fake", "connector", "crd")
+	app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, fake, connector, istio-gateway, cloudfoundry, crd").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "istio-gateway", "cloudfoundry", "fake", "connector", "crd")
 	app.Flag("namespace", "Limit sources of endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace)
 	app.Flag("annotation-filter", "Filter sources managed by external-dns via annotation using label selector semantics (default: all sources)").Default(defaultConfig.AnnotationFilter).StringVar(&cfg.AnnotationFilter)
 	app.Flag("fqdn-template", "A templated string that's used to generate DNS names from sources that don't define a hostname themselves, or to add a hostname suffix when paired with the fake source (optional). Accepts comma separated list for multiple global FQDN.").Default(defaultConfig.FQDNTemplate).StringVar(&cfg.FQDNTemplate)
diff --git a/source/cloudfoundry.go b/source/cloudfoundry.go
new file mode 100644
index 00000000..8d8270c4
--- /dev/null
+++ b/source/cloudfoundry.go
@@ -0,0 +1,59 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package source
+
+import (
+	"net/url"
+
+	cfclient "github.com/cloudfoundry-community/go-cfclient"
+	"github.com/kubernetes-incubator/external-dns/endpoint"
+)
+
+type cloudfoundrySource struct {
+	client *cfclient.Client
+	config Config
+}
+
+// NewCloudFoundrySource creates a new cloudfoundrySource with the given config
+func NewCloudFoundrySource(cfClient *cfclient.Client) (Source, error) {
+	return &cloudfoundrySource{
+		client: cfClient,
+	}, nil
+}
+
+// Endpoints returns endpoint objects
+func (rs *cloudfoundrySource) Endpoints() ([]*endpoint.Endpoint, error) {
+	endpoints := []*endpoint.Endpoint{}
+
+	u, err := url.Parse(rs.client.Config.ApiAddress)
+	if err != nil {
+		panic(err)
+	}
+
+	domains, _ := rs.client.ListDomains()
+	for _, domain := range domains {
+		q := url.Values{}
+		q.Set("q", "domain_guid:"+domain.Guid)
+		routes, _ := rs.client.ListRoutesByQuery(q)
+		for _, element := range routes {
+			endpoints = append(endpoints,
+				endpoint.NewEndpointWithTTL(element.Host+"."+domain.Name, endpoint.RecordTypeCNAME, 300, u.Host))
+		}
+	}
+
+	return endpoints, nil
+}
diff --git a/source/store.go b/source/store.go
index 4a143e13..5491085e 100644
--- a/source/store.go
+++ b/source/store.go
@@ -25,6 +25,7 @@ import (
 
 	"sync"
 
+	cfclient "github.com/cloudfoundry-community/go-cfclient"
 	"github.com/linki/instrumented_http"
 	log "github.com/sirupsen/logrus"
 	istiocrd "istio.io/istio/pilot/pkg/config/kube/crd"
@@ -53,12 +54,16 @@ type Config struct {
 	KubeMaster                  string
 	ServiceTypeFilter           []string
 	IstioIngressGatewayServices []string
+	CFAPIEndpoint               string
+	CFUsername                  string
+	CFPassword                  string
 }
 
 // ClientGenerator provides clients
 type ClientGenerator interface {
 	KubeClient() (kubernetes.Interface, error)
 	IstioClient() (istiomodel.ConfigStore, error)
+	CloudFoundryClient(cfAPPEndpoint string, cfUsername string, cfPassword string) (*cfclient.Client, error)
 }
 
 // SingletonClientGenerator stores provider clients and guarantees that only one instance of client
@@ -69,8 +74,10 @@ type SingletonClientGenerator struct {
 	RequestTimeout time.Duration
 	kubeClient     kubernetes.Interface
 	istioClient    istiomodel.ConfigStore
+	cfClient       *cfclient.Client
 	kubeOnce       sync.Once
 	istioOnce      sync.Once
+	cfOnce         sync.Once
 }
 
 // KubeClient generates a kube client if it was not created before
@@ -91,6 +98,30 @@ func (p *SingletonClientGenerator) IstioClient() (istiomodel.ConfigStore, error)
 	return p.istioClient, err
 }
 
+// CloudFoundryClient generates a cf client if it was not created before
+func (p *SingletonClientGenerator) CloudFoundryClient(cfAPIEndpoint string, cfUsername string, cfPassword string) (*cfclient.Client, error) {
+	var err error
+	p.cfOnce.Do(func() {
+		p.cfClient, err = NewCFClient(cfAPIEndpoint, cfUsername, cfPassword)
+	})
+	return p.cfClient, err
+}
+
+// NewCFClient return a new CF client object.
+func NewCFClient(cfAPIEndpoint string, cfUsername string, cfPassword string) (*cfclient.Client, error) {
+	c := &cfclient.Config{
+		ApiAddress: "https://" + cfAPIEndpoint,
+		Username:   cfUsername,
+		Password:   cfPassword,
+	}
+	client, err := cfclient.NewClient(c)
+	if err != nil {
+		return nil, err
+	}
+
+	return client, nil
+}
+
 // ByNames returns multiple Sources given multiple names.
 func ByNames(p ClientGenerator, names []string, cfg *Config) ([]Source, error) {
 	sources := []Source{}
@@ -130,6 +161,12 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
 			return nil, err
 		}
 		return NewIstioGatewaySource(kubernetesClient, istioClient, cfg.IstioIngressGatewayServices, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
+	case "cloudfoundry":
+		cfClient, err := p.CloudFoundryClient(cfg.CFAPIEndpoint, cfg.CFUsername, cfg.CFPassword)
+		if err != nil {
+			return nil, err
+		}
+		return NewCloudFoundrySource(cfClient)
 	case "fake":
 		return NewFakeSource(cfg.FQDNTemplate)
 	case "connector":


Test patch:
diff --git a/source/cloudfoundry_test.go b/source/cloudfoundry_test.go
new file mode 100644
index 00000000..59efc8e6
--- /dev/null
+++ b/source/cloudfoundry_test.go
@@ -0,0 +1,38 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package source
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"github.com/stretchr/testify/suite"
+)
+
+type RouteSuite struct {
+	suite.Suite
+}
+
+func TestRouteSource(t *testing.T) {
+	suite.Run(t, new(RouteSuite))
+	t.Run("Interface", testRouteSourceImplementsSource)
+}
+
+// testRouteSourceImplementsSource tests that cloudfoundrySource is a valid Source.
+func testRouteSourceImplementsSource(t *testing.T) {
+	require.Implements(t, (*Source)(nil), new(cloudfoundrySource))
+}
diff --git a/source/store_test.go b/source/store_test.go
index 08045a0a..176ac9bb 100644
--- a/source/store_test.go
+++ b/source/store_test.go
@@ -24,14 +24,16 @@ import (
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/kubernetes/fake"
 
+	cfclient "github.com/cloudfoundry-community/go-cfclient"
 	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/suite"
 )
 
 type MockClientGenerator struct {
 	mock.Mock
-	kubeClient  kubernetes.Interface
-	istioClient istiomodel.ConfigStore
+	kubeClient         kubernetes.Interface
+	istioClient        istiomodel.ConfigStore
+	cloudFoundryClient *cfclient.Client
 }
 
 func (m *MockClientGenerator) KubeClient() (kubernetes.Interface, error) {
@@ -52,6 +54,15 @@ func (m *MockClientGenerator) IstioClient() (istiomodel.ConfigStore, error) {
 	return nil, args.Error(1)
 }
 
+func (m *MockClientGenerator) CloudFoundryClient(cfAPIEndpoint string, cfUsername string, cfPassword string) (*cfclient.Client, error) {
+	args := m.Called()
+	if args.Error(1) == nil {
+		m.cloudFoundryClient = args.Get(0).(*cfclient.Client)
+		return m.cloudFoundryClient, nil
+	}
+	return nil, args.Error(1)
+}
+
 type ByNamesTestSuite struct {
 	suite.Suite
 }