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

⚠️ change structure to store crdVersion and webhookversion (go/v3-alpha) #1899

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
110 changes: 85 additions & 25 deletions pkg/model/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type Config struct {

// Resources tracks scaffolded resources in the project
// This info is tracked only in project with version 2
Resources []GVK `json:"resources,omitempty"`
Resources []ResourceData `json:"resources,omitempty"`

// Multigroup tracks if the project has more than one group
MultiGroup bool `json:"multigroup,omitempty"`
Expand Down Expand Up @@ -78,32 +78,30 @@ func (c Config) IsV3() bool {
return c.Version == Version3Alpha
}

// HasResource returns true if API resource is already tracked
func (c Config) HasResource(target GVK) bool {
// GetResource returns the GKV if the resource is found
func (c Config) GetResource(target ResourceData) *ResourceData {
// Return true if the target resource is found in the tracked resources
for _, r := range c.Resources {
if r.isEqualTo(target) {
return true
if r.isGVKEqualTo(target) {
return &r
}
}

// Return false otherwise
return false
return nil
}

// UpdateResources either adds gvk to the tracked set or, if the resource already exists,
// updates the the equivalent resource in the set.
func (c *Config) UpdateResources(gvk GVK) {
func (c *Config) UpdateResources(resource ResourceData) {
// If the resource already exists, update it.
for i, r := range c.Resources {
if r.isEqualTo(gvk) {
c.Resources[i].merge(gvk)
if r.isGVKEqualTo(resource) {
c.Resources[i].merge(resource)
return
}
}

// The resource does not exist, append the resource to the tracked ones.
c.Resources = append(c.Resources, gvk)
c.Resources = append(c.Resources, resource)
}

// HasGroup returns true if group is already tracked
Expand Down Expand Up @@ -136,9 +134,13 @@ func (c Config) resourceAPIVersionCompatible(verType, version string) bool {
var currVersion string
switch verType {
case "crd":
currVersion = res.CRDVersion
if res.API != nil {
currVersion = res.API.CRDVersion
}
case "webhook":
currVersion = res.WebhookVersion
if res.Webhooks != nil {
currVersion = res.Webhooks.WebhookVersion
}
}
if currVersion != "" && version != currVersion {
return false
Expand All @@ -147,33 +149,71 @@ func (c Config) resourceAPIVersionCompatible(verType, version string) bool {
return true
}

// GVK contains information about scaffolded resources
type GVK struct {
// ResourceData contains information about scaffolded resources
type ResourceData struct {
Group string `json:"group,omitempty"`
Version string `json:"version,omitempty"`
Kind string `json:"kind,omitempty"`

// CRDVersion holds the CustomResourceDefinition API version used for the GVK.
// API holds the API data
API *API `json:"api,omitempty"`

// Webhooks holds the Webhooks data
Webhooks *Webhooks `json:"webhooks,omitempty"`
}

// API contains information about scaffolded APIs
type API struct {
// CRDVersion holds the CustomResourceDefinition API version used for the ResourceData.
CRDVersion string `json:"crdVersion,omitempty"`
// WebhookVersion holds the {Validating,Mutating}WebhookConfiguration API version used for the GVK.
}

// Webhooks contains information about scaffolded webhooks
type Webhooks struct {
// WebhookVersion holds the {Validating,Mutating}WebhookConfiguration API version used for the Options.
WebhookVersion string `json:"webhookVersion,omitempty"`
}

// isEqualTo compares it with another resource
func (r GVK) isEqualTo(other GVK) bool {
// isGVKEqualTo compares it with another resource
func (r ResourceData) isGVKEqualTo(other ResourceData) bool {
return r.Group == other.Group &&
r.Version == other.Version &&
r.Kind == other.Kind
}

// merge combines fields of two GVKs that have matching group, version, and kind,
// favoring the receiver's values.
func (r *GVK) merge(other GVK) {
if r.CRDVersion == "" && other.CRDVersion != "" {
r.CRDVersion = other.CRDVersion
func (r *ResourceData) merge(other ResourceData) {
if other.Webhooks != nil {
if r.Webhooks == nil {
r.Webhooks = other.Webhooks
} else {
r.Webhooks.merge(other.Webhooks)
}
}
if r.WebhookVersion == "" && other.WebhookVersion != "" {
r.WebhookVersion = other.WebhookVersion

if other.API != nil {
if r.API == nil {
r.API = other.API
} else {
r.API.merge(other.API)
}
}
}

// merge compares it with another webhook by setting each webhook type individually so existing values are
// not overwritten.
func (w *Webhooks) merge(other *Webhooks) {
if w.WebhookVersion == "" && other.WebhookVersion != "" {
w.WebhookVersion = other.WebhookVersion
}
}

// merge compares it with another api by setting each api type individually so existing values are
// not overwritten.
func (a *API) merge(other *API) {
if a.CRDVersion == "" && other.CRDVersion != "" {
a.CRDVersion = other.CRDVersion
}
}

Expand All @@ -182,6 +222,26 @@ func (c Config) Marshal() ([]byte, error) {
// Ignore extra fields at first.
cfg := c
cfg.Plugins = nil

// Ignore some fields if v2.
if cfg.IsV2() {
for i := range cfg.Resources {
cfg.Resources[i].API = nil
cfg.Resources[i].Webhooks = nil
}
}

for i, r := range cfg.Resources {
// If API is empty, omit it (prevents `api: {}`).
if r.API != nil && r.API.CRDVersion == "" {
cfg.Resources[i].API = nil
}
// If Webhooks is empty, omit it (prevents `webhooks: {}`).
if r.Webhooks != nil && r.Webhooks.WebhookVersion == "" {
cfg.Resources[i].Webhooks = nil
}
}

content, err := yaml.Marshal(cfg)
if err != nil {
return nil, fmt.Errorf("error marshalling project configuration: %v", err)
Expand Down
105 changes: 73 additions & 32 deletions pkg/model/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@ var _ = Describe("PluginConfig", func() {
Data1 string `json:"data-1"`
Data2 string `json:"data-2"`
}
const defaultWebhookVersion = "v1"

resource := ResourceData{Group: "Foo", Kind: "Baz", Version: "v1"}
resource.Webhooks = &Webhooks{defaultWebhookVersion}

It("should return true when has the ResourceData is equals", func() {
Expect(resource.isGVKEqualTo(ResourceData{Group: "Foo", Kind: "Baz", Version: "v1"})).To(BeTrue())
})

It("should return false when ResourceData is NOT equals", func() {
Expect(resource.isGVKEqualTo(ResourceData{Group: "Foo", Kind: "Baz", Version: "v2"})).To(BeFalse())
})

It("IsV2 should return true when the config is V2", func() {
cfg := Config{Version: Version2}
Expect(cfg.IsV2()).To(BeTrue())
})

It("IsV3 should return true when the config is V3", func() {
cfg := Config{Version: Version3Alpha}
Expect(cfg.IsV3()).To(BeTrue())
})

It("should encode correctly", func() {
var (
Expand Down Expand Up @@ -165,56 +187,61 @@ var _ = Describe("PluginConfig", func() {
})
})

var _ = Describe("Resource Version Compatibility", func() {
var _ = Describe("ResourceData Version Compatibility", func() {

var (
c *Config
gvk1, gvk2 GVK
c *Config
resource1, resource2 ResourceData

defaultVersion = "v1"
)

BeforeEach(func() {
c = &Config{}
gvk1 = GVK{Group: "example", Version: "v1", Kind: "TestKind"}
gvk2 = GVK{Group: "example", Version: "v1", Kind: "TestKind2"}
resource1 = ResourceData{Group: "example", Version: "v1", Kind: "TestKind"}
resource2 = ResourceData{Group: "example", Version: "v1", Kind: "TestKind2"}
})

Context("resourceAPIVersionCompatible", func() {
It("returns true for a list of empty resources", func() {
Expect(c.resourceAPIVersionCompatible("crd", defaultVersion)).To(BeTrue())
Expect(c.resourceAPIVersionCompatible("webhook", defaultVersion)).To(BeTrue())
})
It("returns true for one resource with an empty version", func() {
c.Resources = []GVK{gvk1}
c.Resources = []ResourceData{resource1}
Expect(c.resourceAPIVersionCompatible("crd", defaultVersion)).To(BeTrue())
Expect(c.resourceAPIVersionCompatible("webhook", defaultVersion)).To(BeTrue())
})
It("returns true for one resource with matching version", func() {
gvk1.CRDVersion = defaultVersion
c.Resources = []GVK{gvk1}
resource1.API = &API{CRDVersion: defaultVersion}
resource1.Webhooks = &Webhooks{WebhookVersion: defaultVersion}
c.Resources = []ResourceData{resource1}
Expect(c.resourceAPIVersionCompatible("crd", defaultVersion)).To(BeTrue())
Expect(c.resourceAPIVersionCompatible("webhook", defaultVersion)).To(BeTrue())
})
It("returns true for two resources with matching versions", func() {
gvk1.CRDVersion = defaultVersion
gvk2.CRDVersion = defaultVersion
c.Resources = []GVK{gvk1, gvk2}
resource1.API = &API{CRDVersion: defaultVersion}
resource1.Webhooks = &Webhooks{WebhookVersion: defaultVersion}
resource2.API = &API{CRDVersion: defaultVersion}
resource2.Webhooks = &Webhooks{WebhookVersion: defaultVersion}
c.Resources = []ResourceData{resource1, resource2}
Expect(c.resourceAPIVersionCompatible("crd", defaultVersion)).To(BeTrue())
Expect(c.resourceAPIVersionCompatible("webhook", defaultVersion)).To(BeTrue())
})
It("returns false for one resource with a non-matching version", func() {
gvk1.CRDVersion = v1beta1
c.Resources = []GVK{gvk1}
resource1.API = &API{CRDVersion: v1beta1}
resource1.Webhooks = &Webhooks{WebhookVersion: v1beta1}
c.Resources = []ResourceData{resource1}
Expect(c.resourceAPIVersionCompatible("crd", defaultVersion)).To(BeFalse())
Expect(c.resourceAPIVersionCompatible("webhook", defaultVersion)).To(BeFalse())
})
It("returns false for two resources containing a non-matching version", func() {
gvk1.CRDVersion = v1beta1
gvk2.CRDVersion = defaultVersion
c.Resources = []GVK{gvk1, gvk2}
resource1.API = &API{CRDVersion: v1beta1}
resource1.Webhooks = &Webhooks{WebhookVersion: v1beta1}
resource2.API = &API{CRDVersion: defaultVersion}
resource2.Webhooks = &Webhooks{WebhookVersion: defaultVersion}
c.Resources = []ResourceData{resource1, resource2}
Expect(c.resourceAPIVersionCompatible("crd", defaultVersion)).To(BeFalse())
})

It("returns false for two resources containing a non-matching version (webhooks)", func() {
gvk1.WebhookVersion = v1beta1
gvk2.WebhookVersion = defaultVersion
c.Resources = []GVK{gvk1, gvk2}
Expect(c.resourceAPIVersionCompatible("webhook", defaultVersion)).To(BeFalse())
})
})
Expand All @@ -223,35 +250,49 @@ var _ = Describe("Resource Version Compatibility", func() {
var _ = Describe("Config", func() {
var (
c *Config
gvk1, gvk2 GVK
gvk1, gvk2 ResourceData
)

BeforeEach(func() {
c = &Config{}
gvk1 = GVK{Group: "example", Version: "v1", Kind: "TestKind"}
gvk2 = GVK{Group: "example", Version: "v1", Kind: "TestKind2"}
gvk1 = ResourceData{Group: "example", Version: "v1", Kind: "TestKind"}
gvk2 = ResourceData{Group: "example", Version: "v1", Kind: "TestKind2"}
})

Context("UpdateResource", func() {
It("Adds a non-existing resource", func() {
c.UpdateResources(gvk1)
Expect(c.Resources).To(Equal([]GVK{gvk1}))
Expect(c.Resources).To(Equal([]ResourceData{gvk1}))
// Update again to ensure idempotency.
c.UpdateResources(gvk1)
Expect(c.Resources).To(Equal([]GVK{gvk1}))
Expect(c.Resources).To(Equal([]ResourceData{gvk1}))
})
It("Updates an existing resource", func() {
c.UpdateResources(gvk1)
gvk := GVK{Group: gvk1.Group, Version: gvk1.Version, Kind: gvk1.Kind, CRDVersion: "v1"}
c.UpdateResources(gvk)
Expect(c.Resources).To(Equal([]GVK{gvk}))
resource := ResourceData{Group: gvk1.Group, Version: gvk1.Version, Kind: gvk1.Kind}
c.UpdateResources(resource)
Expect(c.Resources).To(Equal([]ResourceData{resource}))
})
It("Updates an existing resource with more than one resource present", func() {
c.UpdateResources(gvk1)
c.UpdateResources(gvk2)
gvk := GVK{Group: gvk1.Group, Version: gvk1.Version, Kind: gvk1.Kind, CRDVersion: "v1"}
gvk := ResourceData{Group: gvk1.Group, Version: gvk1.Version, Kind: gvk1.Kind}
c.UpdateResources(gvk)
Expect(c.Resources).To(Equal([]GVK{gvk, gvk2}))
Expect(c.Resources).To(Equal([]ResourceData{gvk, gvk2}))
})
})

Context("HasGroup", func() {
It("should return true when config has a resource with the group", func() {
c.UpdateResources(gvk1)
Expect(c.Resources).To(Equal([]ResourceData{gvk1}))
Expect(c.HasGroup(gvk1.Group)).To(BeTrue())
})
It("should return false when config has a resource with not the same group", func() {
c.UpdateResources(gvk1)
Expect(c.Resources).To(Equal([]ResourceData{gvk1}))
Expect(c.HasGroup("hasNot")).To(BeFalse())
})

})
})
Loading