Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
90640ec
Update CHANGELOG/CHANGELOG-1.35.md for v1.35.5
k8s-release-robot May 12, 2026
350f793
DRA: fix AllocationModeAll with consumed counters
takonomura May 8, 2026
9f52f4d
controller/selinuxwarning: Pre-parse SELinux label
tchap Feb 24, 2026
fd08821
controller/selinuxwarning/cache: Add reverse index
tchap Feb 24, 2026
4a0532b
Cache selinux conflicts
gnufied May 11, 2026
b596e7b
Restore ability to plumb binary data through envvar values
liggitt May 19, 2026
f1ce76d
feat(volume): add IsRemount to MounterArgs
aramase May 13, 2026
df562ae
fix(csi): preserve mount dir on NodePublish error during remount
aramase May 13, 2026
77292b8
fix(endpoint): avoid panic on services with empty IPFamilies
rahulbabu95 Apr 22, 2026
a74c5bf
Fix DRA scoring bug with mixed allocated and unallocated claims
johnbelamaric May 12, 2026
d2212b8
kubeadm: fix dry-run CA copy paths in init certs
ErikJiang May 28, 2026
d44082b
Fix wrong marking of errors
lalitc375 Jun 3, 2026
79ce171
Merge pull request #139193 from liggitt/env-binary-1.35
k8s-ci-robot Jun 8, 2026
607d6c1
Merge pull request #139137 from gnufied/manual-cherry-pick-selinux-me…
k8s-ci-robot Jun 8, 2026
e8003b4
Merge pull request #139234 from rahulbabu95/automated-cherry-pick-of-…
k8s-ci-robot Jun 8, 2026
3fd20ce
Merge pull request #139362 from nojnhuh/automated-cherry-pick-of-#139…
k8s-ci-robot Jun 8, 2026
f5419c1
Merge pull request #138989 from pohly/automated-cherry-pick-of-#13888…
k8s-ci-robot Jun 8, 2026
508ff96
Merge pull request #139229 from aramase/automated-cherry-pick-of-#139…
k8s-ci-robot Jun 8, 2026
4eb3220
Merge pull request #139509 from lalitc375/automated-cherry-pick-of-#1…
k8s-ci-robot Jun 8, 2026
ccae5e3
Merge pull request #139447 from HirazawaUi/automated-cherry-pick-of-#…
k8s-ci-robot Jun 8, 2026
5ccf8f4
Bump images and versions to go 1.25.11 and distroless iptables
cpanato Jun 9, 2026
6b479f6
Merge pull request #139588 from cpanato/update-go-images-rel135
k8s-ci-robot Jun 9, 2026
fc0e7a6
Release commit for Kubernetes v1.35.6
k8s-release-robot Jun 11, 2026
531189a
Merge tag 'v1.35.6' into release-4.22
redhat-chai-bot Jun 29, 2026
7694c3f
UPSTREAM: <drop>: hack/update-vendor.sh, make update and update image
redhat-chai-bot Jun 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .go-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.25.9
1.25.11
236 changes: 171 additions & 65 deletions CHANGELOG/CHANGELOG-1.35.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/build-image/cross/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.35.0-go1.25.9-bullseye.0
v1.35.0-go1.25.11-bullseye.0
4 changes: 2 additions & 2 deletions build/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ readonly REMOTE_OUTPUT_BINPATH="${REMOTE_OUTPUT_SUBPATH}/bin"
readonly REMOTE_OUTPUT_GOPATH="${REMOTE_OUTPUT_SUBPATH}/go"

# These are the default versions (image tags) for their respective base images.
readonly __default_distroless_iptables_version=v0.8.9
readonly __default_go_runner_version=v2.4.0-go1.25.9-bookworm.0
readonly __default_distroless_iptables_version=v0.8.11
readonly __default_go_runner_version=v2.4.0-go1.25.11-bookworm.0
readonly __default_setcap_version=bookworm-v1.0.6

# The default image for all binaries which are dynamically linked.
Expand Down
6 changes: 3 additions & 3 deletions build/dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ dependencies:
# should also be updated, but go-runner is much harder to exploit and has
# far less relevancy to go updates for Kubernetes more generally.
- name: "registry.k8s.io/kube-cross: dependents"
version: v1.35.0-go1.25.9-bullseye.0
version: v1.35.0-go1.25.11-bullseye.0
refPaths:
- path: build/build-image/cross/VERSION

Expand Down Expand Up @@ -170,15 +170,15 @@ dependencies:
match: registry\.k8s\.io\/build-image\/debian-base:[a-zA-Z]+\-v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)

- name: "registry.k8s.io/distroless-iptables: dependents"
version: v0.8.9
version: v0.8.11
refPaths:
- path: build/common.sh
match: __default_distroless_iptables_version=
- path: test/utils/image/manifest.go
match: configs\[DistrolessIptables\] = Config{list\.BuildImageRegistry, "distroless-iptables", "v([0-9]+)\.([0-9]+)\.([0-9]+)"}

- name: "registry.k8s.io/go-runner: dependents"
version: v2.4.0-go1.25.9-bookworm.0
version: v2.4.0-go1.25.11-bookworm.0
refPaths:
- path: build/common.sh
match: __default_go_runner_version=
Expand Down
14 changes: 10 additions & 4 deletions cmd/kubeadm/app/cmd/phases/init/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package phases

import (
"fmt"
"os"
"path/filepath"
"strings"

Expand Down Expand Up @@ -216,20 +217,25 @@ func runCAPhase(ca *certsphase.KubeadmCert) func(c workflow.RunData) error {

if cert, err := pkiutil.TryLoadCertFromDisk(data.CertificateDir(), ca.BaseName); err == nil {
certsphase.CheckCertificatePeriodValidity(ca.BaseName, cert)
srcCertPath, srcKeyPath := pkiutil.PathsForCertAndKey(data.CertificateDir(), ca.BaseName)
dryRunCertPath, dryRunKeyPath := pkiutil.PathsForCertAndKey(data.CertificateWriteDir(), ca.BaseName)

// If CA Cert existed while dryrun, copy CA Cert to dryrun dir for later use
if data.DryRun() {
err := kubeadmutil.CopyFile(filepath.Join(data.CertificateDir(), kubeadmconstants.CACertName), filepath.Join(data.CertificateWriteDir(), kubeadmconstants.CACertName))
if err := os.MkdirAll(filepath.Dir(dryRunCertPath), os.FileMode(0700)); err != nil {
return errors.Wrapf(err, "failed to create directory %s", filepath.Dir(dryRunCertPath))
}
err := kubeadmutil.CopyFile(srcCertPath, dryRunCertPath)
if err != nil {
return errors.Wrapf(err, "could not copy %s to dry run directory %s", kubeadmconstants.CACertName, data.CertificateWriteDir())
return errors.Wrapf(err, "could not copy %s to dry run directory %s", fmt.Sprintf("%s.crt", ca.BaseName), data.CertificateWriteDir())
}
}
if _, err := pkiutil.TryLoadKeyFromDisk(data.CertificateDir(), ca.BaseName); err == nil {
// If CA Key existed while dryrun, copy CA Key to dryrun dir for later use
if data.DryRun() {
err := kubeadmutil.CopyFile(filepath.Join(data.CertificateDir(), kubeadmconstants.CAKeyName), filepath.Join(data.CertificateWriteDir(), kubeadmconstants.CAKeyName))
err := kubeadmutil.CopyFile(srcKeyPath, dryRunKeyPath)
if err != nil {
return errors.Wrapf(err, "could not copy %s to dry run directory %s", kubeadmconstants.CAKeyName, data.CertificateWriteDir())
return errors.Wrapf(err, "could not copy %s to dry run directory %s", fmt.Sprintf("%s.key", ca.BaseName), data.CertificateWriteDir())
}
}
fmt.Printf("[certs] Using existing %s certificate authority\n", ca.BaseName)
Expand Down
55 changes: 55 additions & 0 deletions cmd/kubeadm/app/cmd/phases/init/certs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ limitations under the License.
package phases

import (
"os"
"path/filepath"
"testing"

"github.com/spf13/cobra"

kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
certstestutil "k8s.io/kubernetes/cmd/kubeadm/app/util/certs"
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
pkiutiltesting "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil/testing"
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
)
Expand All @@ -33,10 +37,19 @@ type testCertsData struct {
cfg *kubeadmapi.InitConfiguration
}

type testDryRunCertsData struct {
testCertsData
certificateDir string
certificateWriteDir string
}

func (t *testCertsData) Cfg() *kubeadmapi.InitConfiguration { return t.cfg }
func (t *testCertsData) ExternalCA() bool { return false }
func (t *testCertsData) CertificateDir() string { return t.cfg.CertificatesDir }
func (t *testCertsData) CertificateWriteDir() string { return t.cfg.CertificatesDir }
func (t *testDryRunCertsData) DryRun() bool { return true }
func (t *testDryRunCertsData) CertificateDir() string { return t.certificateDir }
func (t *testDryRunCertsData) CertificateWriteDir() string { return t.certificateWriteDir }

func TestCreateSparseCerts(t *testing.T) {
for _, test := range certstestutil.GetSparseCertTestCases(t) {
Expand All @@ -63,3 +76,45 @@ func TestCreateSparseCerts(t *testing.T) {
})
}
}

func TestRunCAPhaseCopiesExistingCAFilesToDryRunDir(t *testing.T) {
for _, ca := range []*certsphase.KubeadmCert{
certsphase.KubeadmCertRootCA(),
certsphase.KubeadmCertFrontProxyCA(),
certsphase.KubeadmCertEtcdCA(),
} {
t.Run(ca.Name, func(t *testing.T) {
pkiutiltesting.Reset()

sourceDir := t.TempDir()
writeDir := t.TempDir()
caCert, caKey := certstestutil.SetupCertificateAuthority(t)
certPath, _ := pkiutil.PathsForCertAndKey(sourceDir, ca.BaseName)
if err := os.MkdirAll(filepath.Dir(certPath), os.FileMode(0700)); err != nil {
t.Fatalf("failed to create source directory for %s: %v", ca.BaseName, err)
}
if err := pkiutil.WriteCertAndKey(sourceDir, ca.BaseName, caCert, caKey); err != nil {
t.Fatalf("failed to write source CA files for %s: %v", ca.BaseName, err)
}

cfg := testutil.GetDefaultInternalConfig(t)
cfg.CertificatesDir = sourceDir
data := &testDryRunCertsData{
testCertsData: testCertsData{cfg: cfg},
certificateDir: sourceDir,
certificateWriteDir: writeDir,
}

if err := runCAPhase(ca)(data); err != nil {
t.Fatalf("runCAPhase(%s) returned error: %v", ca.Name, err)
}

if _, err := pkiutil.TryLoadCertFromDisk(writeDir, ca.BaseName); err != nil {
t.Fatalf("expected copied cert for %s in dry-run dir: %v", ca.BaseName, err)
}
if _, err := pkiutil.TryLoadKeyFromDisk(writeDir, ca.BaseName); err != nil {
t.Fatalf("expected copied key for %s in dry-run dir: %v", ca.BaseName, err)
}
})
}
}
2 changes: 1 addition & 1 deletion openshift-hack/images/hyperkube/Dockerfile.rhel
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ COPY --from=builder /tmp/build/* /usr/bin/
LABEL io.k8s.display-name="OpenShift Kubernetes Server Commands" \
io.k8s.description="OpenShift is a platform for developing, building, and deploying containerized applications." \
io.openshift.tags="openshift,hyperkube" \
io.openshift.build.versions="kubernetes=1.35.5"
io.openshift.build.versions="kubernetes=1.35.6"
6 changes: 3 additions & 3 deletions pkg/apis/core/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func ValidateQualifiedName(value string, fldPath *field.Path) field.ErrorList {
func ValidateDNS1123SubdomainWithUnderScore(value string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, msg := range validation.IsDNS1123SubdomainWithUnderscore(value) {
allErrs = append(allErrs, field.Invalid(fldPath, value, msg)).WithOrigin("format=k8s-dns-subdomain-with-underscore")
allErrs = append(allErrs, field.Invalid(fldPath, value, msg).WithOrigin("format=k8s-dns-subdomain-with-underscore"))
}
return allErrs
}
Expand All @@ -185,7 +185,7 @@ func ValidateDNS1123SubdomainWithUnderScore(value string, fldPath *field.Path) f
func ValidateDNS1123Subdomain(value string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, msg := range validation.IsDNS1123Subdomain(value) {
allErrs = append(allErrs, field.Invalid(fldPath, value, msg)).WithOrigin("format=k8s-long-name")
allErrs = append(allErrs, field.Invalid(fldPath, value, msg).WithOrigin("format=k8s-long-name"))
}
return allErrs
}
Expand Down Expand Up @@ -4977,7 +4977,7 @@ func ValidateNodeSelectorRequirement(rq core.NodeSelectorRequirement, allowInval
path := fldPath.Child("values")
for valueIndex, value := range rq.Values {
for _, msg := range validation.IsValidLabelValue(value) {
allErrs = append(allErrs, field.Invalid(path.Index(valueIndex), value, msg)).WithOrigin("format=k8s-label-value")
allErrs = append(allErrs, field.Invalid(path.Index(valueIndex), value, msg).WithOrigin("format=k8s-label-value"))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/resource/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ func validateCounterSet(counterSet resource.CounterSet, fldPath *field.Path) fie
if counterSet.Name == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "").MarkCoveredByDeclarative())
} else {
allErrs = append(allErrs, validateCounterName(counterSet.Name, fldPath.Child("name"))...).MarkCoveredByDeclarative()
allErrs = append(allErrs, validateCounterName(counterSet.Name, fldPath.Child("name")).MarkCoveredByDeclarative()...)
}
if len(counterSet.Counters) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("counters"), ""))
Expand Down Expand Up @@ -874,7 +874,7 @@ func validateDeviceCounterConsumption(deviceCounterConsumption resource.DeviceCo
if len(deviceCounterConsumption.CounterSet) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("counterSet"), "").MarkCoveredByDeclarative())
} else {
allErrs = append(allErrs, validateCounterName(deviceCounterConsumption.CounterSet, fldPath.Child("counterSet"))...).MarkCoveredByDeclarative()
allErrs = append(allErrs, validateCounterName(deviceCounterConsumption.CounterSet, fldPath.Child("counterSet")).MarkCoveredByDeclarative()...)
}
if len(deviceCounterConsumption.Counters) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("counters"), ""))
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/storage/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func ValidateStorageClassUpdate(storageClass, oldStorageClass *storage.StorageCl
func validateProvisioner(provisioner string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(provisioner) == 0 {
allErrs = append(allErrs, field.Required(fldPath, provisioner)).MarkCoveredByDeclarative()
allErrs = append(allErrs, field.Required(fldPath, provisioner).MarkCoveredByDeclarative())
}
if len(provisioner) > 0 {
allErrs = append(allErrs, apivalidation.ValidateQualifiedName(strings.ToLower(provisioner), fldPath)...)
Expand Down
18 changes: 16 additions & 2 deletions pkg/controller/endpoint/endpoints_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,22 @@ func (e *Controller) Run(ctx context.Context, workers int) {

func podToEndpointAddressForService(svc *v1.Service, pod *v1.Pod) (*v1.EndpointAddress, error) {
var endpointIP string

wantIPv6 := svc.Spec.IPFamilies[0] == v1.IPv6Protocol
ipFamily := v1.IPv4Protocol

// IPFamilies is expected to be populated by apiserver defaulting, but
// some services may reach this controller with an empty IPFamilies via
// watch events. Infer the family from ClusterIP or
// pod IP so we never panic on IPFamilies[0].
if len(svc.Spec.IPFamilies) > 0 {
ipFamily = svc.Spec.IPFamilies[0]
} else if len(svc.Spec.ClusterIP) > 0 && svc.Spec.ClusterIP != v1.ClusterIPNone {
if utilnet.IsIPv6String(svc.Spec.ClusterIP) {
ipFamily = v1.IPv6Protocol
}
} else if utilnet.IsIPv6String(pod.Status.PodIP) {
ipFamily = v1.IPv6Protocol
}
wantIPv6 := ipFamily == v1.IPv6Protocol

// Find an IP that matches the family. We parse and restringify the IP in case the
// value on the Pod is in an irregular format.
Expand Down
80 changes: 80 additions & 0 deletions pkg/controller/endpoint/endpoints_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3224,3 +3224,83 @@ func TestSyncEndpointsAddDeletePorts(t *testing.T) {
t.Fatalf("incorrect endpoints after deleting first port:\n%s", diff)
}
}

func TestPodToEndpointAddressForServiceEmptyIPFamilies(t *testing.T) {
testCases := []struct {
name string
clusterIP string
podIPs []v1.PodIP
podIP string
wantErr bool
wantFamily v1.IPFamily
}{
{
name: "headful IPv4, IPv4 pod",
clusterIP: "10.0.0.1",
podIPs: []v1.PodIP{{IP: "10.244.0.1"}},
wantFamily: v1.IPv4Protocol,
},
{
name: "headful IPv6, IPv6 pod",
clusterIP: "fd00::1",
podIPs: []v1.PodIP{{IP: "fd00::10"}},
wantFamily: v1.IPv6Protocol,
},
{
name: "headful IPv4, no matching pod IP",
clusterIP: "10.0.0.1",
podIPs: []v1.PodIP{{IP: "fd00::10"}},
wantErr: true,
},
{
name: "headless, IPv4 pod",
clusterIP: v1.ClusterIPNone,
podIPs: []v1.PodIP{{IP: "10.244.0.1"}},
podIP: "10.244.0.1",
wantFamily: v1.IPv4Protocol,
},
{
name: "headless, IPv6 pod",
clusterIP: v1.ClusterIPNone,
podIPs: []v1.PodIP{{IP: "fd00::10"}},
podIP: "fd00::10",
wantFamily: v1.IPv6Protocol,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
svc := &v1.Service{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: v1.ServiceSpec{
// Intentionally leave IPFamilies empty.
ClusterIP: tc.clusterIP,
},
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo-pod", Namespace: "bar", UID: "uid-1"},
Spec: v1.PodSpec{NodeName: "node-1"},
Status: v1.PodStatus{PodIP: tc.podIP, PodIPs: tc.podIPs},
}

addr, err := podToEndpointAddressForService(svc, pod)
if tc.wantErr {
if err == nil {
t.Fatalf("expected error but got addr=%v", addr)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if addr == nil {
t.Fatal("expected an address but got nil")
}
isV6 := utilnet.IsIPv6String(addr.IP)
wantV6 := tc.wantFamily == v1.IPv6Protocol
if isV6 != wantV6 {
t.Errorf("got IP %q (IPv6=%v), want family %v", addr.IP, isV6, tc.wantFamily)
}
})
}
}
2 changes: 1 addition & 1 deletion pkg/kubelet/container/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ type Image struct {
// EnvVar represents the environment variable.
type EnvVar struct {
Name string
Value string
Value string // TODO: switch to []byte
}

// Annotation represents an annotation.
Expand Down
5 changes: 4 additions & 1 deletion pkg/kubelet/kubelet_pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
var (
configMaps = make(map[string]*v1.ConfigMap)
secrets = make(map[string]*v1.Secret)
tmpEnv = make(map[string]string)
tmpEnv = make(map[string]string) // TODO: switch to map[string][]byte
)

// Env will override EnvFrom variables.
Expand Down Expand Up @@ -798,6 +798,7 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
k = envFrom.Prefix + k
}

// TODO: validate no NUL bytes
tmpEnv[k] = v
}
case envFrom.SecretRef != nil:
Expand Down Expand Up @@ -825,6 +826,7 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
k = envFrom.Prefix + k
}

// TODO: validate no NUL bytes
tmpEnv[k] = string(v)
}
}
Expand Down Expand Up @@ -918,6 +920,7 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
}
return result, fmt.Errorf("couldn't find key %v in Secret %v/%v", key, pod.Namespace, name)
}
// TODO: validate no NUL bytes
runtimeVal = string(runtimeValBytes)
case utilfeature.DefaultFeatureGate.Enabled(features.EnvFiles) && envVar.ValueFrom.FileKeyRef != nil:
f := envVar.ValueFrom.FileKeyRef
Expand Down
3 changes: 2 additions & 1 deletion pkg/kubelet/kuberuntime/kuberuntime_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"time"

codes "google.golang.org/grpc/codes"

crierror "k8s.io/cri-api/pkg/errors"

"github.com/opencontainers/selinux/go-selinux"
Expand Down Expand Up @@ -400,7 +401,7 @@ func (m *kubeGenericRuntimeManager) generateContainerConfig(ctx context.Context,
e := opts.Envs[idx]
envs[idx] = &runtimeapi.KeyValue{
Key: e.Name,
Value: e.Value,
Value: []byte(e.Value),
}
}
config.Envs = envs
Expand Down
Loading