Skip to content

Commit

Permalink
Merge pull request istio#6 from openshift-service-mesh-bot/none-maste…
Browse files Browse the repository at this point in the history
…r-merge_upstream_istio_master-6253864e

Automator: merge upstream changes to openshift-service-mesh/istio@master
  • Loading branch information
openshift-merge-bot[bot] authored Apr 26, 2024
2 parents 56e3b2b + c349bbf commit 8e3a019
Show file tree
Hide file tree
Showing 34 changed files with 1,004 additions and 318 deletions.
8 changes: 4 additions & 4 deletions architecture/ambient/ztunnel.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ However, this has one glaring issue: how do we get the traffic to the remote pro

A secondary goal was to enable a smoother on-ramp from "Zero" to "Getting some value".
Historically, Istio had to really be consumed all-or-nothing for things to work as expected.
In particular, an easy answer to "I just want to get have mTLS everywhere, then I can think about adopting the rest of service mesh" was desired.
In particular, an easy answer to "I just want to have mTLS everywhere, then I can think about adopting the rest of service mesh" was desired.

## Goals

Expand Down Expand Up @@ -67,7 +67,7 @@ The address type has the following goals:
* Specifically, ztunnel should be able to send a request to the control plane to answer "I got a request to send traffic to 1.1.1.1, what is 1.1.1.1?"
* While this is not needed for small scales, this is important for the long tail of massive clusters (think 1 million endpoints), where the entire set of endpoints cannot reasonably be replicated to each ztunnel.
* It should not be client-specific.
* In Istio sidecars, historically we had a lot of client-specific XDS. For example, putting the XDS-client's IP back into the XDS response. This makes efficient control plane implementation (most notably, caching), extremely challenging.
* In Istio sidecars, historically we had a lot of client-specific xDS. For example, putting the xDS-client's IP back into the xDS response. This makes efficient control plane implementation (most notably, caching), extremely challenging.
* In practice, this largely means that references are fully qualified in the API. IP Addresses (generally) have a network associated with them, node names have a cluster associated with them, etc.

See the [XDS Evolution](https://docs.google.com/document/d/1V5wkeBHbLSLMzAMbwFlFZNHdZPyUEspG4lHbnB0UaCg/edit) document for more history and details.
Expand All @@ -76,7 +76,7 @@ The `Workload` aims to represent everything about a workload (generally a `Pod`
This includes things like its IP address, identity, metadata (name, namespace, app, version, etc), and whether it has a waypoint proxy associated.

The `Service` aims to represent everything about a service (generally a `Service` or `ServiceEntry`).
This includes things like its IP addresses and ports.
This includes things like its IP addresses, ports and an associated waypoint proxy if it has one.

### Authorization Type

Expand Down Expand Up @@ -243,7 +243,7 @@ This CA enforcement is done by Istio's CA, and is a requirement for any alternat
Note: Ztunnel authenticates to the CA with a Kubernetes Service Account JWT token, which encodes the pod information, which is what enables this.

Ztunnel will request certificates for all identities on the node.
It determines this based on the Workload XDS configuration it receives.
It determines this based on the Workload xDS configuration it receives.
When a new identity is discovered on the node, it will be enqueued for fetching at a low priority, as an optimization.
However, if a request needs a certain identity that we have not fetched yet, it will be immediately requested.

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ require (
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda
google.golang.org/grpc v1.63.0
google.golang.org/grpc v1.63.2
google.golang.org/protobuf v1.33.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v2 v2.4.0
Expand All @@ -118,7 +118,7 @@ require (
k8s.io/kubectl v0.30.0
k8s.io/utils v0.0.0-20240310230437-4693a0247e57
sigs.k8s.io/controller-runtime v0.17.1-0.20240412100902-45e166d1fd01
sigs.k8s.io/gateway-api v1.0.1-0.20240418002011-7f9f51098f40
sigs.k8s.io/gateway-api v1.1.0-rc1.0.20240425002700-732025730290
sigs.k8s.io/mcs-api v0.1.0
sigs.k8s.io/yaml v1.4.0
)
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1045,8 +1045,8 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
Expand Down Expand Up @@ -1159,8 +1159,8 @@ sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gE
sigs.k8s.io/controller-runtime v0.17.1-0.20240412100902-45e166d1fd01 h1:YIERVtPSYjTgFZSJEs4JlsTmS9UYtsDF5RHwbLiwwy8=
sigs.k8s.io/controller-runtime v0.17.1-0.20240412100902-45e166d1fd01/go.mod h1:qnlXuoQTuzXfYBI1t3qz6oc85Ce+QPAqEcHkQBggWQI=
sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
sigs.k8s.io/gateway-api v1.0.1-0.20240418002011-7f9f51098f40 h1:gH4Z10+yqcxTJ52r1Spk8rhj3Kv0IevbdPPvPgEcqbA=
sigs.k8s.io/gateway-api v1.0.1-0.20240418002011-7f9f51098f40/go.mod h1:pOW7gKjeVN1xDfoPYkzI1b3v0XtgTcIx/CJ8cQAbOCA=
sigs.k8s.io/gateway-api v1.1.0-rc1.0.20240425002700-732025730290 h1:Z0My9zcCWwJOzXFB7uhE3jVyckfjflZ2BWAp2btS8h4=
sigs.k8s.io/gateway-api v1.1.0-rc1.0.20240425002700-732025730290/go.mod h1:l6CLPHMssBJ+FGeSqxZGuy/RsyDUXjbc0QAmgkUiRoc=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kind v0.8.1/go.mod h1:oNKTxUVPYkV9lWzY6CVMNluVq8cBsyq+UgPJdvA3uu4=
Expand Down
4 changes: 2 additions & 2 deletions istio.deps
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
"name": "PROXY_REPO_SHA",
"repoName": "proxy",
"file": "",
"lastStableSHA": "22f0f1295c038d8336576455836c420ed2d8d906"
"lastStableSHA": "f0c8cb2a2aba6b03686d2206e2266ab3eb8c8fa7"
},
{
"_comment": "",
"name": "ZTUNNEL_REPO_SHA",
"repoName": "ztunnel",
"file": "",
"lastStableSHA": "1b006e93c232769cff8f3d7d61cf01593d2a3578"
"lastStableSHA": "4549e63e2d5120c4a386ea41288dd08b9f823fc9"
}
]
19 changes: 8 additions & 11 deletions istioctl/pkg/writer/ztunnel/configdump/certificates.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,17 @@ func (c *ConfigWriter) PrintSecretSummary() error {
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\n",
secret.Identity, valueOrNA(""), secret.State, false, valueOrNA(""), valueOrNA(""), valueOrNA(""))
} else {
// get the CA value and remove it from the cert chain slice so it's not printed twice
ca := secret.CertChain[0]
secret.CertChain = secret.CertChain[1:]
n := new(big.Int)
n, _ = n.SetString(ca.SerialNumber, 10)
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%x\t%v\t%v\n",
secret.Identity, "CA", secret.State, certNotExpired(ca), n, valueOrNA(ca.ExpirationTime), valueOrNA(ca.ValidFrom))

// print the rest of the cert chain
for _, ca := range secret.CertChain {
for i, ca := range secret.CertChain {
t := "Intermediate"
if i == 0 {
t = "Leaf"
} else if i == len(secret.CertChain)-1 {
t = "Root"
}
n := new(big.Int)
n, _ = n.SetString(ca.SerialNumber, 10)
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%x\t%v\t%v\n",
secret.Identity, "Cert Chain", secret.State, certNotExpired(ca), n, valueOrNA(ca.ExpirationTime), valueOrNA(ca.ValidFrom))
secret.Identity, t, secret.State, certNotExpired(ca), n, valueOrNA(ca.ExpirationTime), valueOrNA(ca.ValidFrom))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
CERTIFICATE NAME TYPE STATUS VALID CERT SERIAL NUMBER NOT AFTER NOT BEFORE
spiffe://cluster.local/ns/istio-system/sa/istiod CA Available true e5dfb59150b2ba7f108d93dcec5aa613 2033-03-22T13:04:57Z 2023-03-21T13:02:57Z
spiffe://cluster.local/ns/istio-system/sa/istiod Cert Chain Available false 8a516645c40ce76c2c0d27ab4e2461c1 2022-03-18T13:04:49Z 2022-03-21T13:04:49Z
spiffe://cluster.local/ns/istio-system/sa/ztunnel NA Initializing false NA NA NA
spiffe://cluster.local/ns/istio-system/sa/another-sa NA Unavailable false NA NA NA
spiffe://cluster.local/ns/istio-system/sa/istiod NA Unavailable false NA NA NA
CERTIFICATE NAME TYPE STATUS VALID CERT SERIAL NUMBER NOT AFTER NOT BEFORE
spiffe://cluster.local/ns/istio-system/sa/istiod Leaf Available true e5dfb59150b2ba7f108d93dcec5aa613 2033-03-22T13:04:57Z 2023-03-21T13:02:57Z
spiffe://cluster.local/ns/istio-system/sa/istiod Root Available false 8a516645c40ce76c2c0d27ab4e2461c1 2022-03-18T13:04:49Z 2022-03-21T13:04:49Z
spiffe://cluster.local/ns/istio-system/sa/ztunnel NA Initializing false NA NA NA
spiffe://cluster.local/ns/istio-system/sa/another-sa NA Unavailable false NA NA NA
spiffe://cluster.local/ns/istio-system/sa/istiod NA Unavailable false NA NA NA
14 changes: 8 additions & 6 deletions pilot/pkg/config/kube/crd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package crd

import (
"encoding/json"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
Expand All @@ -23,17 +25,17 @@ import (
type IstioKind struct {
metav1.TypeMeta
metav1.ObjectMeta `json:"metadata"`
Spec map[string]any `json:"spec"`
Status map[string]any `json:"status,omitempty"`
Spec json.RawMessage `json:"spec"`
Status *json.RawMessage `json:"status,omitempty"`
}

// GetSpec from a wrapper
func (in *IstioKind) GetSpec() map[string]any {
func (in *IstioKind) GetSpec() json.RawMessage {
return in.Spec
}

// GetStatus from a wrapper
func (in *IstioKind) GetStatus() map[string]any {
func (in *IstioKind) GetStatus() *json.RawMessage {
return in.Status
}

Expand Down Expand Up @@ -73,7 +75,7 @@ func (in *IstioKind) DeepCopyObject() runtime.Object {
// IstioObject is a k8s wrapper interface for config objects
type IstioObject interface {
runtime.Object
GetSpec() map[string]any
GetStatus() map[string]any
GetSpec() json.RawMessage
GetStatus() *json.RawMessage
GetObjectMeta() metav1.ObjectMeta
}
6 changes: 4 additions & 2 deletions pilot/pkg/config/kube/crd/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,26 @@
package crd_test

import (
"encoding/json"
"reflect"
"testing"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"istio.io/istio/pilot/pkg/config/kube/crd"
"istio.io/istio/pkg/ptr"
)

func TestKind(t *testing.T) {
obj := crd.IstioKind{}

spec := map[string]any{"a": "b"}
spec := json.RawMessage(`{"a":"b"}`)
obj.Spec = spec
if got := obj.GetSpec(); !reflect.DeepEqual(spec, got) {
t.Errorf("GetSpec() => got %v, want %v", got, spec)
}

status := map[string]any{"yo": "lit"}
status := ptr.Of(json.RawMessage(`{"c":"d"}`))
obj.Status = status
if got := obj.GetStatus(); !reflect.DeepEqual(status, got) {
t.Errorf("GetStatus() => got %v, want %v", got, status)
Expand Down
17 changes: 12 additions & 5 deletions pilot/pkg/config/kube/crd/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func FromJSON(s resource.Schema, js string) (config.Spec, error) {
return c, nil
}

func StatusJSONFromMap(schema resource.Schema, jsonMap map[string]any) (config.Status, error) {
func StatusJSONFromMap(schema resource.Schema, jsonMap *json.RawMessage) (config.Status, error) {
if jsonMap == nil {
return nil, nil
}
Expand Down Expand Up @@ -124,13 +124,20 @@ func ConvertObject(schema resource.Schema, object IstioObject, domain string) (*

// ConvertConfig translates Istio config to k8s config JSON
func ConvertConfig(cfg config.Config) (IstioObject, error) {
spec, err := config.ToMap(cfg.Spec)
spec, err := config.ToRaw(cfg.Spec)
if err != nil {
return nil, err
}
status, err := config.ToMap(cfg.Status)
if err != nil {
return nil, err
var status *json.RawMessage
if cfg.Status != nil {
s, err := config.ToRaw(cfg.Status)
if err != nil {
return nil, err
}
// Probably a bit overkill, but this ensures we marshal a pointer to an empty object (&empty{}) as nil
if !bytes.Equal(s, []byte("{}")) {
status = &s
}
}
namespace := cfg.Namespace
if namespace == "" {
Expand Down
3 changes: 2 additions & 1 deletion pilot/pkg/config/kube/crd/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package crd

import (
"encoding/json"
"testing"

gateway "sigs.k8s.io/gateway-api/apis/v1beta1"
Expand All @@ -28,7 +29,7 @@ import (
)

func TestConvertIstioKind(t *testing.T) {
if _, err := ConvertObject(collections.VirtualService, &IstioKind{Spec: map[string]any{"x": 1}}, "local"); err != nil {
if _, err := ConvertObject(collections.VirtualService, &IstioKind{Spec: json.RawMessage(`{"x":1}`)}, "local"); err != nil {
t.Errorf("error for converting object: %s", err)
}
}
Expand Down
9 changes: 8 additions & 1 deletion pilot/pkg/config/kube/crdclient/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"go.uber.org/atomic"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/gateway-api/pkg/consts"

"istio.io/api/meta/v1alpha1"
"istio.io/api/networking/v1alpha3"
Expand All @@ -49,7 +50,13 @@ func makeClient(t *testing.T, schemas collection.Schemas, f kubetypes.DynamicObj
kube.SetObjectFilter(fake, f)
}
for _, s := range schemas.All() {
clienttest.MakeCRD(t, fake, s.GroupVersionResource())
var annotations map[string]string
if s.Group() == gvk.KubernetesGateway.Group {
annotations = map[string]string{
consts.BundleVersionAnnotation: consts.BundleVersion,
}
}
clienttest.MakeCRDWithAnnotations(t, fake, s.GroupVersionResource(), annotations)
}
stop := test.NewStop(t)
config := New(fake, Option{})
Expand Down
70 changes: 36 additions & 34 deletions pilot/pkg/model/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"istio.io/istio/pilot/pkg/serviceregistry/util/label"
"istio.io/istio/pilot/pkg/trustbundle"
networkutil "istio.io/istio/pilot/pkg/util/network"
v3 "istio.io/istio/pilot/pkg/xds/v3"
"istio.io/istio/pkg/cluster"
"istio.io/istio/pkg/config/constants"
"istio.io/istio/pkg/config/host"
Expand All @@ -51,6 +52,7 @@ import (
netutil "istio.io/istio/pkg/util/net"
"istio.io/istio/pkg/util/protomarshal"
"istio.io/istio/pkg/util/sets"
"istio.io/istio/pkg/xds"
)

type (
Expand Down Expand Up @@ -410,40 +412,7 @@ type Proxy struct {
LastPushTime time.Time
}

// WatchedResource tracks an active DiscoveryRequest subscription.
type WatchedResource struct {
// TypeUrl is copied from the DiscoveryRequest.TypeUrl that initiated watching this resource.
// nolint
TypeUrl string

// ResourceNames tracks the list of resources that are actively watched.
// For LDS and CDS, all resources of the TypeUrl type are watched if it is empty.
// For endpoints the resource names will have list of clusters and for clusters it is empty.
// For Delta Xds, all resources of the TypeUrl that a client has subscribed to.
ResourceNames []string

// Wildcard indicates the subscription is a wildcard subscription. This only applies to types that
// allow both wildcard and non-wildcard subscriptions.
Wildcard bool

// NonceSent is the nonce sent in the last sent response. If it is equal with NonceAcked, the
// last message has been processed. If empty: we never sent a message of this type.
NonceSent string

// NonceAcked is the last acked message.
NonceAcked string

// AlwaysRespond, if true, will ensure that even when a request would otherwise be treated as an
// ACK, it will be responded to. This typically happens when a proxy reconnects to another instance of
// Istiod. In that case, Envoy expects us to respond to EDS/RDS/SDS requests to finish warming of
// clusters/listeners.
// Typically, this should be set to 'false' after response; keeping it true would likely result in an endless loop.
AlwaysRespond bool

// LastResources tracks the contents of the last push.
// This field is extremely expensive to maintain and is typically disabled
LastResources Resources
}
type WatchedResource = xds.WatchedResource

var istioVersionRegexp = regexp.MustCompile(`^([1-9]+)\.([0-9]+)(\.([0-9]+))?`)

Expand Down Expand Up @@ -962,6 +931,39 @@ func (node *Proxy) GetWatchedResource(typeURL string) *WatchedResource {
return node.WatchedResources[typeURL]
}

func (node *Proxy) NewWatchedResource(typeURL string, names []string) {
node.Lock()
defer node.Unlock()

node.WatchedResources[typeURL] = &WatchedResource{TypeUrl: typeURL, ResourceNames: names}
// For all EDS requests that we have already responded with in the same stream let us
// force the response. It is important to respond to those requests for Envoy to finish
// warming of those resources(Clusters).
// This can happen with the following sequence
// 1. Envoy disconnects and reconnects to Istiod.
// 2. Envoy sends EDS request and we respond with it.
// 3. Envoy sends CDS request and we respond with clusters.
// 4. Envoy detects a change in cluster state and tries to warm those clusters and send EDS request for them.
// 5. We should respond to the EDS request with Endpoints to let Envoy finish cluster warming.
// Refer to https://github.com/envoyproxy/envoy/issues/13009 for more details.
for _, dependent := range WarmingDependencies(typeURL) {
if dwr, exists := node.WatchedResources[dependent]; exists {
dwr.AlwaysRespond = true
}
}
}

// WarmingDependencies returns the dependent typeURLs that need to be responded with
// for warming of this typeURL.
func WarmingDependencies(typeURL string) []string {
switch typeURL {
case v3.ClusterType:
return []string{v3.EndpointType}
default:
return nil
}
}

func (node *Proxy) AddOrUpdateWatchedResource(r *WatchedResource) {
if r == nil {
return
Expand Down
Loading

0 comments on commit 8e3a019

Please sign in to comment.