# mcp-over-xds

在今年五月份社区已经添加了 [MCP-OVER-XDS的实现](https://github.com/istio/istio/pull/28634) ，在当前的master代码中已经 [移除了mcp 协议](https://github.com/istio/istio/pull/28634) 的实现代码，将全部转换为MCP-OVER-XDS实现，也就意味着istio 1.9将不再支持原有MCP协议，具体参考 [XDS-OVER-MCP设计](https://docs.google.com/document/d/1lHjUzDY-4hxElWN7g6pz-_Ws7yIPt62tmX3iGs_uLyI/edit)

## initConfigSources

当我们配置的ConfigSource为XDS类型时，将创建XDS client,用于发起请求

```go
// 初始化一个ads client
xdsMCP, err := adsc.New(srcAddress.Host, &adsc.Config{
    Meta: model.NodeMetadata{
        Generator: "api",
    }.ToStruct(),
    InitialDiscoveryRequests: adsc.ConfigInitialRequests(),
})
if err != nil {
    return fmt.Errorf("failed to dial XDS %s %v", configSource.Address, err)
}
// 初始化一个configstore
store := memory.Make(collections.Pilot)
// 初始化config controller
configController := memory.NewController(store)
// 初始化istio config stroe
xdsMCP.Store = model.MakeIstioStore(configController)
// 运行
err = xdsMCP.Run()
if err != nil {
    return fmt.Errorf("MCP: failed running %v", err)
}
s.ConfigStores = append(s.ConfigStores, configController)
log.Warn("Started XDS config ", s.ConfigStores)
```

InitialDiscoveryRequests 用于进行建联后的初始请求，代表着istio所需要关注的资源，

```
out = append(out, &discovery.DiscoveryRequest{
        // meshconfig类型
        TypeUrl: collections.IstioMeshV1Alpha1MeshConfig.Resource().GroupVersionKind().String(),
    })
    for _, sch := range collections.Pilot.All() {
        out = append(out, &discovery.DiscoveryRequest{
            TypeUrl: sch.Resource().GroupVersionKind().String(),
        })
    }
```

Pilot 涉及的所有资源

```
    Pilot = collection.NewSchemasBuilder().
        MustAdd(IstioNetworkingV1Alpha3Destinationrules).
        MustAdd(IstioNetworkingV1Alpha3Envoyfilters).
        MustAdd(IstioNetworkingV1Alpha3Gateways).
        MustAdd(IstioNetworkingV1Alpha3Serviceentries).
        MustAdd(IstioNetworkingV1Alpha3Sidecars).
        MustAdd(IstioNetworkingV1Alpha3Virtualservices).
        MustAdd(IstioNetworkingV1Alpha3Workloadentries).
        MustAdd(IstioNetworkingV1Alpha3Workloadgroups).
        MustAdd(IstioSecurityV1Beta1Authorizationpolicies).
        MustAdd(IstioSecurityV1Beta1Peerauthentications).
        MustAdd(IstioSecurityV1Beta1Requestauthentications).
        Build()
```

## ads run

ads run主要进行发送初始发现请求，然后接收返回的数据

```
func (a *ADSC) Run() error {
    var err error
    a.client = discovery.NewAggregatedDiscoveryServiceClient(a.conn)
    a.stream, err = a.client.StreamAggregatedResources(context.Background())
    if err != nil {
        return err
    }
    a.sendNodeMeta = true
    a.InitialLoad = 0
    //  发送初始请求
    for _, r := range a.cfg.InitialDiscoveryRequests {
        if r.TypeUrl == v3.ClusterType {
            a.watchTime = time.Now()
        }
        _ = a.Send(r)
    }
    a.RecvWg.Add(1)
    // 接收ads server返回的数据
    go a.handleRecv()
    return nil
}
```

## handleMCP

因为adsc是一个通用的ads客户端，我们不需要关注其它的逻辑主要关注handleMCP

获取请求的gvk

```
groupVersionKind := config.GroupVersionKind{Group: gvk[0], Version: gvk[1], Kind: gvk[2]}
```

判断cache内是否有对应的对象，有则更新，无则创建

```
cfg := a.Store.Get(val.GroupVersionKind, val.Name, val.Namespace)
        if cfg == nil {
            _, err = a.Store.Create(*val)
            if err != nil {
                adscLog.Warnf("Error adding a new resource to the store %v", err)
                continue
            }
        } else {
            _, err = a.Store.Update(*val)
            if err != nil {
                adscLog.Warnf("Error updating an existing resource in the store %v", err)
                continue
            }
        }
```

当envoy连接时，将从configstore 获取配置列表下发到客户端，当连接后，对于Create或者update类型触发对应的handler,对应的为confighandler来进行push

## MCP-OVER-XDS简单示例

### 部署istio

istioctl manifest generate --set profile=demo > demo.yaml 修改 istio.istio-system configmap添加以下内容

添加xds configsource

```
    configSources:
      - address: xds://172.16.233.1:1109
```

部署istio

```
kubectl apply -f demo.yaml
```

### 实现ads server

对于原生的envoy-control-plane，使用xds.newserver，利用snapshotcache来实现ads server的方式在istio中不适用，因为envoy-control-plane只能管理原生envoy的xds资源，而mcp-over-xds涉及到istio的crd资源

对于一个adsserver 来说需要实现AggregatedDiscoveryServiceServer接口

```
type AggregatedDiscoveryServiceServer interface {
    // This is a gRPC-only API.
    StreamAggregatedResources(AggregatedDiscoveryService_StreamAggregatedResourcesServer) error
    DeltaAggregatedResources(AggregatedDiscoveryService_DeltaAggregatedResourcesServer) error
}
```

```
func (m myserver) StreamAggregatedResources(stream d3.AggregatedDiscoveryService_StreamAggregatedResourcesServer) error {
    if peerInfo, ok := peer.FromContext(stream.Context()); ok {
        log.Println(peerInfo)
    }
    pushall(stream)
    for {
        select {
        case <-m.psuhc:
            pushall(stream)
        }
    }
    return nil
}
```

pushall主要了推送对应数据的逻辑,istio会根据TypeUrl进行反解析

```
err = stream.Send(&d3.DiscoveryResponse{
        TypeUrl:     "security.istio.io/v1beta1/PeerAuthentication",
        VersionInfo: "1",
        Nonce:       "",
        Resources:   resp,
    })
```

这里我们统一为客户端也就是istio推送一个PeerAuthentication策略

```
    pa := v1beta1.PeerAuthentication{
        TypeMeta: v1.TypeMeta{
            APIVersion: "security.istio.io/v1beta1",
            Kind:       "PeerAuthentication",
        },
        ObjectMeta: v1.ObjectMeta{
            Name:      "default",
            Namespace: "istio-system",
        },
        Spec: securityv1beta1.PeerAuthentication{
            Mtls: &securityv1beta1.PeerAuthentication_MutualTLS{
                Mode: securityv1beta1.PeerAuthentication_MutualTLS_STRICT,
            },
        },
    }
```

### 检查

* 查看配置是否生效

  我们访问istiod的debug接口可以看到已经收到了对应的策略

```
curl 172.17.116.27:8080/debug/configz
[
    {
      "kind": "PeerAuthentication",
      "apiVersion": "security.istio.io/v1beta1",
      "metadata": {
        "name": "default",
        "namespace": "istio-system",
        "resourceVersion": "2020-12-15 06:17:28.774277383 +0000 UTC m=+782.333181911",
        "creationTimestamp": null
      },
      "spec": {
        "mtls": {}
      }
    }
  ]
```

* 访问服务

部署测试实例

```
kubectl create ns foo
kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n foo
kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n foo
kubectl create ns bar
kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n bar
kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n bar
kubectl create ns legacy
kubectl apply -f samples/httpbin/httpbin.yaml -n legacy
kubectl apply -f samples/sleep/sleep.yaml -n legacy
```

访问服务已经无法正常的访问

```
curl httpbin.foo:8000/ip
curl: (56) Recv failure: Connection reset by peer
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://rocdu.gitbook.io/deep-understanding-of-istio/7/4.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
