Send istioctl CRUD requests through Kubernetes apiserver proxy#669
Send istioctl CRUD requests through Kubernetes apiserver proxy#669ayj merged 11 commits intoistio:masterfrom
Conversation
Use k8s.io/go-client libraries to send Istio API requests through Kubernetes API server proxy. The advantages of this aproach are the following: * User does not need to set ISTIO_MANAGER_ADDRESS. * User does not need to run `kubectl port-forward` in the background * Manager API server is accessed through k8s service instead of a specifc endpoint instance/pod. See https://kubernetes.io/docs/concepts/cluster-administration/access-cluster/#discovering-builtin-services for Kubernetes documentation on this method.
rshriram
left a comment
There was a problem hiding this comment.
@ayj overall, this is a really cool solution. I like it a lot. However, I am concerned that we are specializing the cli completely for k8s (in which case, it should be called istioctl-k8s), when we just managed to decouple the CLI from k8s almost completely (with manager/mixer API servers).
Having a specialized k8s switch will allow us to keep the tool operational in the magic mode (the one you have) and the old mode (current/explicit mode). The explicit mode will be super useful for troubleshooting.
cc @esnible @liamawhite
(please do keep the curl commands around)
| } | ||
|
|
||
| func (m *ManagerClient) toCurl(request *http.Request, body string) string { | ||
|
|
There was a problem hiding this comment.
I think @esnible added this to print the debug output/curl commands, so that users can try the API calls themselves.
There was a problem hiding this comment.
From #645 - istio.io/manager/client/proxy/ManagerClient should log HTTP, similar to what the Kubernetes clients do.
This change uses the Kubernetes client so the logging behavior is exactly like what other Kubernetes clients do, e.g.
$ istioctl -v=999 create -f route-rule-delay.yaml
I0505 09:35:37.315569 132334 loader.go:354] Config loaded from file /usr/local/google/home/jasonyoung/.kube/config
I0505 09:35:37.316597 132334 round_trippers.go:398] curl -k -v -XGET -H "Accept: application/json, */*" -H "User-Agent: istioctl/v0.0.0 (linux/amd64) kubernetes/$Format" -H "Authorization: Basic YWRtaW46WXdRcEVvRzVLV2tzZ1hueQ==" https://35.184.13.238/apis/extensions/v1beta1/thirdpartyresources/istio-config.istio.io
I0505 09:35:37.544263 132334 round_trippers.go:417] GET https://35.184.13.238/apis/extensions/v1beta1/thirdpartyresources/istio-config.istio.io 200 OK in 227 milliseconds
I0505 09:35:37.544285 132334 round_trippers.go:423] Response Headers:
I0505 09:35:37.544298 132334 round_trippers.go:426] Content-Type: application/json
I0505 09:35:37.544306 132334 round_trippers.go:426] Content-Length: 369
I0505 09:35:37.544315 132334 round_trippers.go:426] Date: Fri, 05 May 2017 16:35:37 GMT
I0505 09:35:37.544345 132334 request.go:991] Response Body: {"kind":"ThirdPartyResource","apiVersion":"extensions/v1beta1","metadata":{"name":"istio-config.istio.io","selfLink":"/apis/extensions/v1beta1/thirdpartyresourcesistio-config.istio.io","uid":"21f2d9e2-2f6c-11e7-a278-42010a80017c","resourceVersion":"1471","creationTimestamp":"2017-05-02T19:18:33Z"},"description":"Istio configuration","versions":[{"name":"v1alpha1"}]}
I0505 09:35:37.544539 132334 client.go:173] Resource already exists: "istio-config.istio.io"
I0505 09:35:37.544557 132334 client.go:197] Checking for TPR resources
I0505 09:35:37.544607 132334 round_trippers.go:398] curl -k -v -XGET -H "Accept: application/json, */*" -H "Authorization: Basic YWRtaW46WXdRcEVvRzVLV2tzZ1hueQ==" https://35.184.13.238/apis/istio.io/v1alpha1/istioconfigs
I0505 09:35:37.584012 132334 round_trippers.go:417] GET https://35.184.13.238/apis/istio.io/v1alpha1/istioconfigs 200 OK in 39 milliseconds
I0505 09:35:37.584032 132334 round_trippers.go:423] Response Headers:
I0505 09:35:37.584039 132334 round_trippers.go:426] Content-Length: 2013
I0505 09:35:37.584044 132334 round_trippers.go:426] Date: Fri, 05 May 2017 16:35:37 GMT
I0505 09:35:37.584049 132334 round_trippers.go:426] Content-Type: application/json
I0505 09:35:37.584115 132334 request.go:991] Response Body: {"kind":"IstioConfigList","items":[{"apiVersion":"istio.io/v1alpha1","kind":"IstioConfig","metadata":{"name":"route-rule-details-default","namespace":"default","selfLink":"/apis/istio.io/v1alpha1/namespaces/default/istioconfigs/route-rule-details-default","uid":"d80cdbdd-3136-11e7-a278-42010a80017c","resourceVersion":"424970","creationTimestamp":"2017-05-05T02:02:08Z"},"spec":{"destination":"details.default.svc.cluster.local","precedence":1,"route":[{"tags":{"version":"v1"}}]}},{"apiVersion":"istio.io/v1alpha1","kind":"IstioConfig","metadata":{"name":"route-rule-productpage-default","namespace":"default","selfLink":"/apis/istio.io/v1alpha1/namespaces/default/istioconfigs/route-rule-productpage-default","uid":"d7f7929b-3136-11e7-a278-42010a80017c","resourceVersion":"424967","creationTimestamp":"2017-05-05T02:02:07Z"},"spec":{"destination":"productpage.default.svc.cluster.local","precedence":1,"route":[{"tags":{"version":"v1"}}]}},{"apiVersion":"istio.io/v1alpha1","kind":"IstioConfig","metadata":{"name":"route-rule-ratings-default","namespace":"default","selfLink":"/apis/istio.io/v1alpha1/namespaces/default/istioconfigs/route-rule-ratings-default","uid":"d805db3d-3136-11e7-a278-42010a80017c","resourceVersion":"424969","creationTimestamp":"2017-05-05T02:02:08Z"},"spec":{"destination":"ratings.default.svc.cluster.local","precedence":1,"route":[{"tags":{"version":"v1"}}]}},{"apiVersion":"istio.io/v1alpha1","kind":"IstioConfig","metadata":{"name":"route-rule-reviews-default","namespace":"default","selfLink":"/apis/istio.io/v1alpha1/namespaces/default/istioconfigs/route-rule-reviews-default","uid":"d43208f5-313a-11e7-a278-42010a80017c","resourceVersion":"427446","creationTimestamp":"2017-05-05T02:30:39Z"},"spec":{"destination":"reviews.default.svc.cluster.local","precedence":1,"route":[{"tags":{"version":"v2"},"weight":50},{"tags":{"version":"v3"},"weight":50}]}}],"metadata":{"selfLink":"/apis/istio.io/v1alpha1/istioconfigs","resourceVersion":"501380"},"apiVersion":"istio.io/v1alpha1"}
I0505 09:35:37.584653 132334 decoder.go:224] decoding stream as YAML
I0505 09:35:37.585543 132334 request.go:991] Request Body: {"type":"route-rule","name":"ratings-test-delay","spec":{"destination":"ratings.default.svc.cluster.local","httpFault":{"delay":{"fixedDelay":"7s","percent":100}},"match":{"httpHeaders":{"Cookie":{"regex":"^(.*?;)?(user=test-user)(;.*)?$"}}},"precedence":2,"route":[{"tags":{"version":"v1"}}]}}
I0505 09:35:37.585614 132334 round_trippers.go:398] curl -k -v -XPOST -H "Accept: application/json, */*" -H "Content-Type: application/json" -H "Authorization: Basic YWRtaW46WXdRcEVvRzVLV2tzZ1hueQ==" https://35.184.13.238/api/v1/namespaces/default/services/istio-manager:8081/proxy/v1alpha1/config/route-rule/default/ratings-test-delay
I0505 09:35:37.635058 132334 round_trippers.go:417] POST https://35.184.13.238/api/v1/namespaces/default/services/istio-manager:8081/proxy/v1alpha1/config/route-rule/default/ratings-test-delay 201 Created in 49 milliseconds
I0505 09:35:37.635480 132334 round_trippers.go:423] Response Headers:
I0505 09:35:37.635807 132334 round_trippers.go:426] Content-Type: application/json
I0505 09:35:37.636078 132334 round_trippers.go:426] Date: Fri, 05 May 2017 16:35:37 GMT
I0505 09:35:37.636509 132334 round_trippers.go:426] Content-Length: 436
I0505 09:35:37.636834 132334 request.go:991] Response Body: {
"type": "route-rule",
"name": "ratings-test-delay",
"spec": {
"destination": "ratings.default.svc.cluster.local",
"httpFault": {
"delay": {
"fixedDelay": "7s",
"percent": 100
}
},
"match": {
"httpHeaders": {
"Cookie": {
"regex": "^(.*?;)?(user=test-user)(;.*)?$"
}
}
},
"precedence": 2,
"route": [
{
"tags": {
"version": "v1"
}
}
]
}
}
Created config: route-rule ratings-test-delayThere was a problem hiding this comment.
Oh nice. Then the extra curl kludge is unnecessary for k8s, but needed in generic mode.
| "Namespace where Istio system resides") | ||
| rootCmd.PersistentFlags().IntVar(&apiServerPort, "port", 8081, | ||
| "Manager API server port") | ||
|
|
There was a problem hiding this comment.
While using the kube api client is a fine idea, i think the istioctl tool is going back and forth between platform agnostic mode and becoming platform specific. With the exception of kubeconfig and the namespace/port stuff, one could technically use istioctl against any manager/mixer deployment (kube/vm/mesos). In that spirit, my suggestion would be to keep the existing infrastructure (or shuffle it), such that one can do
istioctl --kube -n foo --istioNamespace bar --...
There was a problem hiding this comment.
The added advantage of this model is that I can always fallback to the current one (directly calling manager/mixer API servers)
There was a problem hiding this comment.
I agree. Seems like a sensible idea would be to have a generic client for fallback as well as a kube client. If we go with a straight kube client now then we're going to have to rewrite a generic client at a later date to achieve platform agnosticism anyway.
There was a problem hiding this comment.
I agree that we should decouple from k8s before it gets even harder to accomplish down the road. That means no k8s dependencies in base istioctl, and the only functional dependency being authentication via JWT token to kube API server proxy. If this is too hard, I'd prefer creating our own JWT validation and using a load balancer IP for the manager config server.
There was a problem hiding this comment.
I don't think there is any disagreement that istioctl should be platform neutral long-term. But I wanted to minimize changes to istioctl and the rest of the Istio install prior to alpha freeze. In particular, accessing kube API server via JWT tokens still requires k8s specific calls to get the token, certs, and API server address vs. using the existing k8s client to do that all for us.
I added the --kube flag to revert to the legacy behavior. Advanced users will need to change the --managerAPIService to localhost: and run kubectl port-forward themselves as before.
iostioctl --kube=false --managerAPIService=http://localhost:8081 create -f route-rule-reviews-v2-v3.yamlThe default behavior is --kube=true.
iostioctl create -f route-rule-reviews-v2-v3.yaml| // (see https://kubernetes.io/docs/concepts/cluster-administration/access-cluster/#discovering-builtin-services) | ||
| func (r *k8sRESTRequester) absPath(string path) string { | ||
| return fmt.Sprintf("api/v1/namespaces/%s/services/istio-manager:%d/proxy/%s/%s", | ||
| r.namespace, r.port, r.version, path) |
There was a problem hiding this comment.
isn't this hard coding the istio-manager service name? Users certainly can rename this service to something else and our system will still work.
There was a problem hiding this comment.
Good point. I generalized apiServerPort to managerAPIService which includes service name and port, e.g. istio-manager:8081
rshriram
left a comment
There was a problem hiding this comment.
I am fine with this. Will leave it to @kyessenov / @liamawhite / @Enible for code comments
|
Will there be any changes on how kube-inject is invoked? If yes, we need to update the documentation. |
| rootCmd.PersistentFlags().StringVar(&istioNamespace, "istioNamespace", defaultIstioNamespace, | ||
| "Namespace where Istio system resides") | ||
| rootCmd.PersistentFlags().StringVar(&istioManagerService, "managerAPIService", "istio-manager:8081", | ||
| "Name of istio-manager service") |
There was a problem hiding this comment.
The meaning of this flag is different based on --kube. Per https://kubernetes.io/docs/concepts/cluster-administration/access-cluster/#discovering-builtin-services it is the service name + port. When --kube=false it is the server address. I updated the flag doc to reflect this.
| absPath := fmt.Sprintf("api/v1/namespaces/%s/services/%s/proxy/%s/%s", | ||
| namespace, service, IstioResourceVersion, path) | ||
| var status int | ||
| outBody, err := cl.dyn.Verb(method). |
There was a problem hiding this comment.
is dyn the right HTTP client? it's configured to use istio TPRs which is not necessary for this client? maybe use cl.client?
There was a problem hiding this comment.
dyn/rest.RESTClient has the HTTP verb methods. cl.client/kubernetes.Clientset doesn't. I could have created an additional RESTClient with Group/Version for k8s.io/v1 just for this purpose, but setting the absolute path seemed to work just as well. We should probably refactor the k8s platform client anyway and parameterize Group/Version since we don't need TPR access in istioctl anymore with proper API server. Then we could replace AbsPath() here with Suffix().
…uests-through-k8s-apiserver-proxy
|
Jenkins job manager/presubmit passed |
…uests-through-k8s-apiserver-proxy
liamawhite
left a comment
There was a problem hiding this comment.
LGTM. Just make sure there is adequate logging in there so we can diagnose easier.
|
Jenkins job manager/presubmit passed |
1 similar comment
|
Jenkins job manager/presubmit passed |
|
Jenkins job manager/presubmit passed |
|
jenkins rebuild manager/e2e-smoketest |
1 similar comment
|
jenkins rebuild manager/e2e-smoketest |
|
Jenkins job manager/e2e-smoketest passed |
* Removes kubectl port-forward dependency for accessing manager/mixer API/config servers. istio/old_pilot_repo#669 adds support to istioctl for accessing manager/mixer API via kubernetes apiserver proxy. kubectl port-forward is no longer needed. * Use --istioNamespace with istioctl commands. istioctl now differentiates the namespace where istio is installed vs. the namespace that is being managed by istio. These are currently the same in the e2e smoke test. * Includes fix for "'panic: shorthand redefinition' for istioctl mixer subcommand"
This is necessary to access the Manager API server via Kubernetes service instead of through pod with `kubectl port-forward` (see istio/old_pilot_repo#669 for corresponding istioctl changes) Former-commit-id: 851e9cb
* Removes `kubectl port-forward` dependency for accessing manager/mixer API/config servers. istio/old_pilot_repo#669 adds support to istioctl for accessing manager/mixer API via kubernetes apiserver proxy. `kubectl port-forward` is no longer needed. * Use --istioNamespace with istioctl commands. istioctl now differentiates the namespace where istio is installed vs. the namespace that is being managed by istio. These are currently the same in the e2e smoke test. * Includes fix for "'panic: shorthand redefinition' for istioctl mixer subcommand" Former-commit-id: 60b4510
* Removes kubectl port-forward dependency for accessing manager/mixer API/config servers. istio/old_pilot_repo#669 adds support to istioctl for accessing manager/mixer API via kubernetes apiserver proxy. kubectl port-forward is no longer needed. * Use --istioNamespace with istioctl commands. istioctl now differentiates the namespace where istio is installed vs. the namespace that is being managed by istio. These are currently the same in the e2e smoke test. * Includes fix for "'panic: shorthand redefinition' for istioctl mixer subcommand" Former-commit-id: 38f44f9
This is necessary to access the Manager API server via Kubernetes service instead of through pod with `kubectl port-forward` (see istio/old_pilot_repo#669 for corresponding istioctl changes) Former-commit-id: 851e9cb
* Removes `kubectl port-forward` dependency for accessing manager/mixer API/config servers. istio/old_pilot_repo#669 adds support to istioctl for accessing manager/mixer API via kubernetes apiserver proxy. `kubectl port-forward` is no longer needed. * Use --istioNamespace with istioctl commands. istioctl now differentiates the namespace where istio is installed vs. the namespace that is being managed by istio. These are currently the same in the e2e smoke test. * Includes fix for "'panic: shorthand redefinition' for istioctl mixer subcommand" Former-commit-id: 60b4510
* Removes kubectl port-forward dependency for accessing manager/mixer API/config servers. istio/old_pilot_repo#669 adds support to istioctl for accessing manager/mixer API via kubernetes apiserver proxy. kubectl port-forward is no longer needed. * Use --istioNamespace with istioctl commands. istioctl now differentiates the namespace where istio is installed vs. the namespace that is being managed by istio. These are currently the same in the e2e smoke test. * Includes fix for "'panic: shorthand redefinition' for istioctl mixer subcommand" Former-commit-id: 38f44f9
Use k8s.io/go-client libraries to send Istio API requests through
Kubernetes API server proxy. The advantages of this aproach are the
following:
User does not need to set ISTIO_MANAGER_ADDRESS.
User does not need to run
kubectl port-forwardin the backgroundManager API server is accessed through k8s service instead of a
specifc endpoint instance/pod.
See https://kubernetes.io/docs/concepts/cluster-administration/access-cluster/#discovering-builtin-services for Kubernetes documentation on this method.