Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make KOS able to handle resources without namespaces #1114

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,10 @@ public class ResourceResponseBuilder
""metadata"": {{
""name"": ""{1}"",
""uid"": ""{2}"",
""namespace"": ""{3}"",
""ownerReferences"": [
{{
""uid"": ""{3}""
""uid"": ""{4}""
}}
]
}}
Expand All @@ -254,6 +255,7 @@ public class ResourceResponseBuilder
private string kind = "";
private string name = "";
private string uid = Guid.NewGuid().ToString();
private string @namespace = "default";
private string ownerUid = Guid.NewGuid().ToString();

public ResourceResponseBuilder WithKind(string kind)
Expand All @@ -274,12 +276,18 @@ public ResourceResponseBuilder WithUid(string uid)
return this;
}

public ResourceResponseBuilder WithNamespace(string @namespace)
{
this.@namespace = @namespace;
return this;
}

public ResourceResponseBuilder WithOwnerUid(string ownerUid)
{
this.ownerUid = ownerUid;
return this;
}

public string Build() => string.Format(template, kind, name, uid, ownerUid);
public string Build() => string.Format(template, kind, name, uid, @namespace, ownerUid);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public async Task ShouldReportStatusesWithIncrementingCheckCount()

for (var i = 0; i < 5; i++)
{
retriever.SetResponses(new List<Resource>{ new TestResource("Pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.InProgress) });
retriever.SetResponses(new List<Resource>{ new TestResource("Pod", "my-pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.InProgress) });
}

var resourceStatusChecker = new RunningResourceStatusCheck(statusCheckTaskFactory, log, new TimeSpan(), new Options(), new[]
Expand All @@ -51,7 +51,7 @@ public async Task SuccessfulBeforeTimeout_ShouldReturnAsSuccessful()
var log = new InMemoryLog();

retriever.SetResponses(
new List<Resource> { new TestResource("Pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.Successful) }
new List<Resource> { new TestResource("Pod", "my-pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.Successful) }
);

var resourceStatusChecker = new RunningResourceStatusCheck(statusCheckTaskFactory, log, new TimeSpan(), new Options(), new[]
Expand All @@ -76,7 +76,7 @@ public async Task FailureBeforeTimeout_ShouldReturnAsFailed()
var log = new InMemoryLog();

retriever.SetResponses(
new List<Resource> { new TestResource("Pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.Failed) }
new List<Resource> { new TestResource("Pod", "my-pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.Failed) }
);

var resourceStatusChecker = new RunningResourceStatusCheck(statusCheckTaskFactory, log, new TimeSpan(), new Options(), new[]
Expand All @@ -102,8 +102,8 @@ public async Task DeploymentInProgressAtTheEndOfTimeout_ShouldReturnAsFailed()
var log = new InMemoryLog();

retriever.SetResponses(
new List<Resource> { new TestResource("Pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.InProgress) },
new List<Resource> { new TestResource("Pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.InProgress) }
new List<Resource> { new TestResource("Pod", "my-pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.InProgress) },
new List<Resource> { new TestResource("Pod", "my-pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.InProgress) }
);

var resourceStatusChecker = new RunningResourceStatusCheck(statusCheckTaskFactory, log, new TimeSpan(), new Options(), new[]
Expand Down Expand Up @@ -131,9 +131,9 @@ public async Task NonTopLevelResourcesAreIgnoredInCalculatingTheDeploymentStatus
retriever.SetResponses(
new List<Resource>
{
new TestResource("ReplicaSet", Kubernetes.ResourceStatus.Resources.ResourceStatus.Successful,
new TestResource("Pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.Failed),
new TestResource("Pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.InProgress))
new TestResource("ReplicaSet", "my-rs", Kubernetes.ResourceStatus.Resources.ResourceStatus.Successful,
new TestResource("Pod", "my-pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.Failed),
new TestResource("Pod", "my-pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.InProgress))
});

var resourceStatusChecker = new RunningResourceStatusCheck(statusCheckTaskFactory, log, new TimeSpan(), new Options(), new[]
Expand All @@ -158,8 +158,8 @@ public async Task ShouldNotReturnSuccessIfSomeOfTheDefinedResourcesWereNotCreate
var log = new InMemoryLog();

retriever.SetResponses(
new List<Resource> { new TestResource("Pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.Successful) },
new List<Resource> { new TestResource("Pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.Successful) }
new List<Resource> { new TestResource("Pod", "my-pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.Successful) },
new List<Resource> { new TestResource("Pod", "my-pod", Kubernetes.ResourceStatus.Resources.ResourceStatus.Successful) }
);

var resourceStatusChecker = new RunningResourceStatusCheck(statusCheckTaskFactory, log, new TimeSpan(), new Options(), new[]
Expand All @@ -175,6 +175,29 @@ public async Task ShouldNotReturnSuccessIfSomeOfTheDefinedResourcesWereNotCreate
.Should().ContainSingle().Which
.Should().Be(RunningResourceStatusCheck.MessageInProgressAtTheEndOfTimeout);
}

[Test]
public async Task ShouldReturnSuccessIfThereAreClusterScopedResources()
{
var retriever = new TestRetriever();
var reporter = new TestReporter();
var kubectl = GetKubectl();
var statusCheckTaskFactory = GetStatusCheckTaskFactory(retriever, reporter, kubectl, maxChecks: 5);
var log = new InMemoryLog();

retriever.SetResponses(
new List<Resource> { new TestResource("crd", "my-crd", Kubernetes.ResourceStatus.Resources.ResourceStatus.Successful) { Namespace = string.Empty } }
);

var resourceStatusChecker = new RunningResourceStatusCheck(statusCheckTaskFactory, log, new TimeSpan(), new Options(), new[]
{
new ResourceIdentifier("crd", "my-crd", "default"),
});

var result = await resourceStatusChecker.WaitForCompletionOrTimeout();

result.Should().BeTrue();
}

private IKubectl GetKubectl()
{
Expand Down Expand Up @@ -239,11 +262,13 @@ public async Task WaitForInterval()

public sealed class TestResource : Resource
{
public TestResource(string kind, Kubernetes.ResourceStatus.Resources.ResourceStatus status, params Resource[] children)
public TestResource(string kind, string name, Kubernetes.ResourceStatus.Resources.ResourceStatus status, params Resource[] children)
{
Name = name;
Uid = Guid.NewGuid().ToString();
Kind = kind;
ResourceStatus = status;
Namespace = "default";
Children = children.ToList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ public static class ResourceLoggingExtensionMethods
{
foreach (var resourceIdentifier in resourceToLog)
{
log.Verbose($" - {resourceIdentifier.Kind}/{resourceIdentifier.Name} in namespace {resourceIdentifier.Namespace}");
var hasNamespace = !string.IsNullOrEmpty(resourceIdentifier.Namespace);
log.Verbose($" - {resourceIdentifier.Kind}/{resourceIdentifier.Name} {(hasNamespace ? $"in namespace {resourceIdentifier.Namespace}" : "")}");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ public class ResourceStatusCheckTask
var definedResourceStatuses = resourceRetriever
.GetAllOwnedResources(definedResources, kubectl, options)
.ToArray();

var nonNamespacedDefinedResources = definedResourceStatuses
.Where(resourceStatus => !resourceStatus.Namespaced)
.Select(resourceStatus => new ResourceIdentifier(
resourceStatus.Kind,
resourceStatus.Name,
resourceStatus.Namespace))
.ToHashSet();

// Filter out cluster-wide resources
definedResources = definedResources
.Where(resource => !string.IsNullOrEmpty(resource.Namespace) && !nonNamespacedDefinedResources.Contains(new ResourceIdentifier(resource.Kind, resource.Name, string.Empty)))
Copy link
Author

@ghost ghost Aug 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If either the namespace on the identifier itself is empty, or the namespace from the resource response is empty, we consider it cluster-scoped.

The namespace on the identifier itself can be empty if the identifier is created by parsing the response of an apply command directly, which is the case when using batch apply with glob patterns.

.ToArray();

var resourceStatuses = definedResourceStatuses
.SelectMany(IterateResourceTree)
.ToDictionary(resource => resource.Uid, resource => resource);
Expand All @@ -85,7 +99,6 @@ public class ResourceStatusCheckTask

private static DeploymentStatus GetDeploymentStatus(Resource[] resources, ResourceIdentifier[] definedResources)
{

if (resources.All(resource => resource.ResourceStatus == ResourceStatus.Resources.ResourceStatus.Successful)
&& resources.Length == definedResources.Length)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ public class Resource : IResourceIdentity

[JsonIgnore] public string Uid { get; set; }
[JsonIgnore] public string Kind { get; set; }
[JsonIgnore] public string Name { get; }
[JsonIgnore] public string Namespace { get; }
[JsonIgnore] public string Name { get; set; }
[JsonIgnore] public string Namespace { get; set; }
[JsonIgnore] public bool Namespaced => !string.IsNullOrEmpty(Namespace);

[JsonIgnore] public virtual ResourceStatus ResourceStatus { get; set; } = ResourceStatus.Successful;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class RunningResourceStatusCheck : IRunningResourceStatusCheck
if (initialResources.Any())
{
log.Verbose("Resource Status Check: Performing resource status checks on the following resources:");
log.Verbose("(Please note that resources that do not have namespaces will be not be included in status checks.)");
log.LogResources(initialResources);
}
else
Expand Down Expand Up @@ -105,6 +106,7 @@ public async Task AddResources(ResourceIdentifier[] newResources)
await statusCheckTask;
statusCheckTask = RunNewStatusCheck(newResources);
log.Verbose($"Resource Status Check: {newResources.Length} new resources have been added:");
log.Verbose("(Please note that resources that do not have namespaces will be not be included in status checks.)");
log.LogResources(newResources);
}
finally
Expand Down