Code optimization and cleanup

This commit is contained in:
sharmapulkit04 2021-08-21 20:36:39 +05:30
parent f527a8c5cb
commit 9594c8e7cd
3 changed files with 114 additions and 127 deletions

View File

@ -27,12 +27,10 @@ import (
"time" "time"
//"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources" //"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
"github.com/jenkinsci/kubernetes-operator/pkg/plugins" "github.com/jenkinsci/kubernetes-operator/pkg/plugins"
"golang.org/x/mod/semver" "golang.org/x/mod/semver"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime" ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log" logf "sigs.k8s.io/controller-runtime/pkg/log"
@ -41,10 +39,12 @@ import (
var ( var (
jenkinslog = logf.Log.WithName("jenkins-resource") // log is for logging in this package. jenkinslog = logf.Log.WithName("jenkins-resource") // log is for logging in this package.
PluginsMgr PluginDataManager = *NewPluginsDataManager("https://ci.jenkins.io/job/Infra/job/plugin-site-api/job/generate-data/lastSuccessfulBuild/artifact/plugins.json.gzip", "/tmp/plugins.json.gzip", "/tmp/plugins.json", false, time.Duration(1000)*time.Second) PluginsMgr PluginDataManager = *NewPluginsDataManager("/tmp/plugins.json.gzip", "/tmp/plugins.json", false, time.Duration(1000)*time.Second)
_ webhook.Validator = &Jenkins{} _ webhook.Validator = &Jenkins{}
) )
const Hosturl = "https://ci.jenkins.io/job/Infra/job/plugin-site-api/job/generate-data/lastSuccessfulBuild/artifact/plugins.json.gzip"
func (in *Jenkins) SetupWebhookWithManager(mgr ctrl.Manager) error { func (in *Jenkins) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr). return ctrl.NewWebhookManagedBy(mgr).
For(in). For(in).
@ -82,13 +82,12 @@ func (in *Jenkins) ValidateDelete() error {
type PluginDataManager struct { type PluginDataManager struct {
PluginDataCache PluginsInfo PluginDataCache PluginsInfo
Hosturl string
Timeout time.Duration Timeout time.Duration
CompressedFilePath string CompressedFilePath string
PluginDataFile string PluginDataFile string
IsCached bool IsCached bool
Attempts int Attempts int
SleepTime int SleepTime time.Duration
} }
type PluginsInfo struct { type PluginsInfo struct {
@ -155,7 +154,7 @@ func Validate(r Jenkins) error {
if len(lastVersion) == 0 { if len(lastVersion) == 0 {
lastVersion = pluginData.Version // setting default value in case of empty string lastVersion = pluginData.Version // setting default value in case of empty string
} }
// checking if this warning applies to our version as well
if compareVersions(firstVersion, lastVersion, pluginData.Version) { if compareVersions(firstVersion, lastVersion, pluginData.Version) {
jenkinslog.Info("Security Vulnerability detected in "+pluginData.Kind+" "+plugin.Name+":"+pluginData.Version, "Warning message", warning.Message, "For more details,check security advisory", warning.URL) jenkinslog.Info("Security Vulnerability detected in "+pluginData.Kind+" "+plugin.Name+":"+pluginData.Version, "Warning message", warning.Message, "For more details,check security advisory", warning.URL)
hasVulnerabilities = true hasVulnerabilities = true
@ -186,9 +185,8 @@ func Validate(r Jenkins) error {
return nil return nil
} }
func NewPluginsDataManager(hosturl string, compressedFilePath string, pluginDataFile string, isCached bool, timeout time.Duration) *PluginDataManager { func NewPluginsDataManager(compressedFilePath string, pluginDataFile string, isCached bool, timeout time.Duration) *PluginDataManager {
return &PluginDataManager{ return &PluginDataManager{
Hosturl: hosturl,
CompressedFilePath: compressedFilePath, CompressedFilePath: compressedFilePath,
PluginDataFile: pluginDataFile, PluginDataFile: pluginDataFile,
IsCached: isCached, IsCached: isCached,
@ -196,63 +194,72 @@ func NewPluginsDataManager(hosturl string, compressedFilePath string, pluginData
} }
} }
// Downloads extracts and reads the JSON data in every 12 hours func (in *PluginDataManager) ManagePluginData(sig chan bool) {
func (in *PluginDataManager) FetchPluginData(isInitialized chan bool) { var isInit bool // checks if the operator
var retryInterval time.Duration
for { for {
jenkinslog.Info("Initializing/Updating the plugin data cache") isCached := in.fetchPluginData()
var isDownloaded, isExtracted, isCached bool if !isInit {
var err error sig <- isCached // sending signal to main to continue
for in.Attempts = 0; in.Attempts < 5; in.Attempts++ { isInit = false
err = in.download()
if err == nil {
isDownloaded = true
break
}
} }
if isDownloaded { in.IsCached = in.IsCached || isCached
for in.Attempts = 0; in.Attempts < 5; in.Attempts++ { if !isCached {
err = in.extract() retryInterval = time.Duration(1) * time.Hour
if err == nil {
isExtracted = true
break
}
}
} else { } else {
jenkinslog.Info("Cache Plugin Data", "failed to download file", err) retryInterval = time.Duration(12) * time.Hour
} }
time.Sleep(retryInterval)
if isExtracted {
for in.Attempts = 0; in.Attempts < 5; in.Attempts++ {
err = in.cache()
if err == nil {
isCached = true
break
}
}
if !isCached {
jenkinslog.Info("Cache Plugin Data", "failed to read plugin data file", err)
}
} else {
jenkinslog.Info("Cache Plugin Data", "failed to extract file", err)
}
// Checks for the first time
if !in.IsCached {
isInitialized <- isCached
in.IsCached = isCached
}
if isCached {
in.SleepTime = 12
} else {
in.SleepTime = 1
}
time.Sleep(time.Duration(in.SleepTime) * time.Hour)
} }
} }
// Downloads extracts and reads the JSON data in every 12 hours
func (in *PluginDataManager) fetchPluginData() bool {
jenkinslog.Info("Initializing/Updating the plugin data cache")
var err error
for in.Attempts = 0; in.Attempts < 5; in.Attempts++ {
err = in.download()
if err == nil {
break
}
jenkinslog.V(1).Info("Cache Plugin Data", "failed to download file", err)
}
if err != nil {
jenkinslog.Info("Cache Plugin Data", "failed to download file", err)
return false
}
for in.Attempts = 0; in.Attempts < 5; in.Attempts++ {
err = in.extract()
if err == nil {
break
}
jenkinslog.V(1).Info("Cache Plugin Data", "failed to extract file", err)
}
if err != nil {
jenkinslog.Info("Cache Plugin Data", "failed to extract file", err)
return false
}
for in.Attempts = 0; in.Attempts < 5; in.Attempts++ {
err = in.cache()
if err == nil {
break
}
jenkinslog.V(1).Info("Cache Plugin Data", "failed to read plugin data file", err)
}
if err != nil {
jenkinslog.Info("Cache Plugin Data", "failed to read plugin data file", err)
return false
}
return true
}
func (in *PluginDataManager) download() error { func (in *PluginDataManager) download() error {
out, err := os.Create(in.CompressedFilePath) out, err := os.Create(in.CompressedFilePath)
if err != nil { if err != nil {
@ -264,11 +271,12 @@ func (in *PluginDataManager) download() error {
Timeout: in.Timeout, Timeout: in.Timeout,
} }
resp, err := client.Get(in.Hosturl) resp, err := client.Get(Hosturl)
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close() defer resp.Body.Close()
_, err = io.Copy(out, resp.Body) _, err = io.Copy(out, resp.Body)
if err != nil { if err != nil {
return err return err
@ -307,7 +315,6 @@ func (in *PluginDataManager) cache() error {
return err return err
} }
defer jsonFile.Close() defer jsonFile.Close()
byteValue, err := ioutil.ReadAll(jsonFile) byteValue, err := ioutil.ReadAll(jsonFile)
if err != nil { if err != nil {
return err return err
@ -337,47 +344,3 @@ func compareVersions(firstVersion string, lastVersion string, pluginVersion stri
} }
return true return true
} }
func CreateJenkinsCR(name string, namespace string, userPlugins []Plugin, validateSecurityWarnings bool) *Jenkins {
jenkins := &Jenkins{
TypeMeta: JenkinsTypeMeta(),
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: JenkinsSpec{
GroovyScripts: GroovyScripts{
Customization: Customization{
Configurations: []ConfigMapRef{},
Secret: SecretRef{
Name: "",
},
},
},
ConfigurationAsCode: ConfigurationAsCode{
Customization: Customization{
Configurations: []ConfigMapRef{},
Secret: SecretRef{
Name: "",
},
},
},
Master: JenkinsMaster{
Plugins: userPlugins,
DisableCSRFProtection: false,
},
ValidateSecurityWarnings: validateSecurityWarnings,
Service: Service{
Type: corev1.ServiceTypeNodePort,
Port: constants.DefaultHTTPPortInt32,
},
JenkinsAPISettings: JenkinsAPISettings{AuthorizationStrategy: CreateUserAuthorizationStrategy},
},
}
return jenkins
}
func CreateSecurityWarnings(firstVersion string, lastVersion string) []Warning {
return []Warning{{Versions: []Version{{FirstVersion: firstVersion, LastVersion: lastVersion}}, ID: "null", Message: "unit testing", URL: "null", Active: false}}
}

View File

@ -5,6 +5,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
func TestMakeSemanticVersion(t *testing.T) { func TestMakeSemanticVersion(t *testing.T) {
@ -77,7 +78,7 @@ func TestCompareVersions(t *testing.T) {
func TestValidate(t *testing.T) { func TestValidate(t *testing.T) {
t.Run("Validating when plugins data file is not fetched", func(t *testing.T) { t.Run("Validating when plugins data file is not fetched", func(t *testing.T) {
userplugins := []Plugin{{Name: "script-security", Version: "1.77"}, {Name: "git-client", Version: "3.9"}, {Name: "git", Version: "4.8.1"}, {Name: "plain-credentials", Version: "1.7"}} userplugins := []Plugin{{Name: "script-security", Version: "1.77"}, {Name: "git-client", Version: "3.9"}, {Name: "git", Version: "4.8.1"}, {Name: "plain-credentials", Version: "1.7"}}
jenkinscr := *CreateJenkinsCR("Jenkins", "test", userplugins, true) jenkinscr := *createJenkinsCR(userplugins, true)
got := jenkinscr.ValidateCreate() got := jenkinscr.ValidateCreate()
assert.Equal(t, got, errors.New("plugins data has not been fetched")) assert.Equal(t, got, errors.New("plugins data has not been fetched"))
}) })
@ -88,66 +89,89 @@ func TestValidate(t *testing.T) {
{Name: "security-script"}, {Name: "security-script"},
{Name: "git-client"}, {Name: "git-client"},
{Name: "git"}, {Name: "git"},
{Name: "google-login", SecurityWarnings: CreateSecurityWarnings("", "1.2")}, {Name: "google-login", SecurityWarnings: createSecurityWarnings("", "1.2")},
{Name: "sample-plugin", SecurityWarnings: CreateSecurityWarnings("", "0.8")}, {Name: "sample-plugin", SecurityWarnings: createSecurityWarnings("", "0.8")},
{Name: "mailer"}, {Name: "mailer"},
{Name: "plain-credentials"}}} {Name: "plain-credentials"}}}
userplugins := []Plugin{{Name: "script-security", Version: "1.77"}, {Name: "git-client", Version: "3.9"}, {Name: "git", Version: "4.8.1"}, {Name: "plain-credentials", Version: "1.7"}} userplugins := []Plugin{{Name: "script-security", Version: "1.77"}, {Name: "git-client", Version: "3.9"}, {Name: "git", Version: "4.8.1"}, {Name: "plain-credentials", Version: "1.7"}}
jenkinscr := *CreateJenkinsCR("Jenkins", "test", userplugins, true) jenkinscr := *createJenkinsCR(userplugins, true)
got := jenkinscr.ValidateCreate() got := jenkinscr.ValidateCreate()
assert.Nil(t, got) assert.Nil(t, got)
}) })
t.Run("Validating a Jenkins CR with some of the plugins having security warnings and validation is turned on", func(t *testing.T) { t.Run("Validating a Jenkins CR with some of the plugins having security warnings and validation is turned on", func(t *testing.T) {
PluginsMgr.PluginDataCache = PluginsInfo{Plugins: []PluginInfo{ PluginsMgr.PluginDataCache = PluginsInfo{Plugins: []PluginInfo{
{Name: "security-script", SecurityWarnings: CreateSecurityWarnings("1.2", "2.2")}, {Name: "security-script", SecurityWarnings: createSecurityWarnings("1.2", "2.2")},
{Name: "workflow-cps", SecurityWarnings: CreateSecurityWarnings("2.59", "")}, {Name: "workflow-cps", SecurityWarnings: createSecurityWarnings("2.59", "")},
{Name: "git-client"}, {Name: "git-client"},
{Name: "git"}, {Name: "git"},
{Name: "sample-plugin", SecurityWarnings: CreateSecurityWarnings("0.8", "")}, {Name: "sample-plugin", SecurityWarnings: createSecurityWarnings("0.8", "")},
{Name: "command-launcher", SecurityWarnings: CreateSecurityWarnings("1.2", "1.4")}, {Name: "command-launcher", SecurityWarnings: createSecurityWarnings("1.2", "1.4")},
{Name: "plain-credentials"}, {Name: "plain-credentials"},
{Name: "google-login", SecurityWarnings: CreateSecurityWarnings("1.1", "1.3")}, {Name: "google-login", SecurityWarnings: createSecurityWarnings("1.1", "1.3")},
{Name: "mailer", SecurityWarnings: CreateSecurityWarnings("1.0.3", "1.1.4")}, {Name: "mailer", SecurityWarnings: createSecurityWarnings("1.0.3", "1.1.4")},
}} }}
userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}} userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
jenkinscr := *CreateJenkinsCR("Jenkins", "test", userplugins, true) jenkinscr := *createJenkinsCR(userplugins, true)
got := jenkinscr.ValidateCreate() got := jenkinscr.ValidateCreate()
assert.Equal(t, got, errors.New("security vulnerabilities detected in the following user-defined plugins: \nworkflow-cps:2.59\ngoogle-login:1.2\nmailer:1.1")) assert.Equal(t, got, errors.New("security vulnerabilities detected in the following user-defined plugins: \nworkflow-cps:2.59\ngoogle-login:1.2\nmailer:1.1"))
}) })
t.Run("Updating a Jenkins CR with some of the plugins having security warnings and validation is turned on", func(t *testing.T) { t.Run("Updating a Jenkins CR with some of the plugins having security warnings and validation is turned on", func(t *testing.T) {
PluginsMgr.PluginDataCache = PluginsInfo{Plugins: []PluginInfo{ PluginsMgr.PluginDataCache = PluginsInfo{Plugins: []PluginInfo{
{Name: "handy-uri-templates-2-api", SecurityWarnings: CreateSecurityWarnings("2.1.8-1.0", "2.2.8-1.0")}, {Name: "handy-uri-templates-2-api", SecurityWarnings: createSecurityWarnings("2.1.8-1.0", "2.2.8-1.0")},
{Name: "workflow-cps", SecurityWarnings: CreateSecurityWarnings("2.59", "")}, {Name: "workflow-cps", SecurityWarnings: createSecurityWarnings("2.59", "")},
{Name: "resource-disposer", SecurityWarnings: CreateSecurityWarnings("0.7", "1.2")}, {Name: "resource-disposer", SecurityWarnings: createSecurityWarnings("0.7", "1.2")},
{Name: "git"}, {Name: "git"},
{Name: "jjwt-api"}, {Name: "jjwt-api"},
{Name: "blueocean-github-pipeline", SecurityWarnings: CreateSecurityWarnings("1.2.0-alpha-2", "1.2.0-beta-5")}, {Name: "blueocean-github-pipeline", SecurityWarnings: createSecurityWarnings("1.2.0-alpha-2", "1.2.0-beta-5")},
{Name: "command-launcher", SecurityWarnings: CreateSecurityWarnings("1.2", "1.4")}, {Name: "command-launcher", SecurityWarnings: createSecurityWarnings("1.2", "1.4")},
{Name: "plain-credentials"}, {Name: "plain-credentials"},
{Name: "ghprb", SecurityWarnings: CreateSecurityWarnings("1.1", "1.43")}, {Name: "ghprb", SecurityWarnings: createSecurityWarnings("1.1", "1.43")},
{Name: "mailer", SecurityWarnings: CreateSecurityWarnings("1.0.3", "1.1.4")}, {Name: "mailer", SecurityWarnings: createSecurityWarnings("1.0.3", "1.1.4")},
}} }}
userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}} userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
oldjenkinscr := *CreateJenkinsCR("Jenkins", "test", userplugins, true) oldjenkinscr := *createJenkinsCR(userplugins, true)
userplugins = []Plugin{{Name: "handy-uri-templates-2-api", Version: "2.1.8-1.0"}, {Name: "resource-disposer", Version: "0.8"}, {Name: "jjwt-api", Version: "0.11.2-9.c8b45b8bb173"}, {Name: "blueocean-github-pipeline", Version: "1.2.0-beta-3"}, {Name: "ghprb", Version: "1.39"}} userplugins = []Plugin{{Name: "handy-uri-templates-2-api", Version: "2.1.8-1.0"}, {Name: "resource-disposer", Version: "0.8"}, {Name: "jjwt-api", Version: "0.11.2-9.c8b45b8bb173"}, {Name: "blueocean-github-pipeline", Version: "1.2.0-beta-3"}, {Name: "ghprb", Version: "1.39"}}
newjenkinscr := *CreateJenkinsCR("Jenkins", "test", userplugins, true) newjenkinscr := *createJenkinsCR(userplugins, true)
got := newjenkinscr.ValidateUpdate(&oldjenkinscr) got := newjenkinscr.ValidateUpdate(&oldjenkinscr)
assert.Equal(t, got, errors.New("security vulnerabilities detected in the following user-defined plugins: \nhandy-uri-templates-2-api:2.1.8-1.0\nresource-disposer:0.8\nblueocean-github-pipeline:1.2.0-beta-3\nghprb:1.39")) assert.Equal(t, got, errors.New("security vulnerabilities detected in the following user-defined plugins: \nhandy-uri-templates-2-api:2.1.8-1.0\nresource-disposer:0.8\nblueocean-github-pipeline:1.2.0-beta-3\nghprb:1.39"))
}) })
t.Run("Validation is turned off", func(t *testing.T) { t.Run("Validation is turned off", func(t *testing.T) {
userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}} userplugins := []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
jenkinscr := *CreateJenkinsCR("Jenkins", "test", userplugins, false) jenkinscr := *createJenkinsCR(userplugins, false)
got := jenkinscr.ValidateCreate() got := jenkinscr.ValidateCreate()
assert.Nil(t, got) assert.Nil(t, got)
userplugins = []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}} userplugins = []Plugin{{Name: "google-login", Version: "1.2"}, {Name: "mailer", Version: "1.1"}, {Name: "git", Version: "4.8.1"}, {Name: "command-launcher", Version: "1.6"}, {Name: "workflow-cps", Version: "2.59"}}
newjenkinscr := *CreateJenkinsCR("jenkins", "test", userplugins, false) newjenkinscr := *createJenkinsCR(userplugins, false)
got = newjenkinscr.ValidateUpdate(&jenkinscr) got = newjenkinscr.ValidateUpdate(&jenkinscr)
assert.Nil(t, got) assert.Nil(t, got)
}) })
} }
func createJenkinsCR(userPlugins []Plugin, validateSecurityWarnings bool) *Jenkins {
jenkins := &Jenkins{
TypeMeta: JenkinsTypeMeta(),
ObjectMeta: metav1.ObjectMeta{
Name: "jenkins",
Namespace: "test",
},
Spec: JenkinsSpec{
Master: JenkinsMaster{
Plugins: userPlugins,
DisableCSRFProtection: false,
},
ValidateSecurityWarnings: validateSecurityWarnings,
},
}
return jenkins
}
func createSecurityWarnings(firstVersion string, lastVersion string) []Warning {
return []Warning{{Versions: []Version{{FirstVersion: firstVersion, LastVersion: lastVersion}}, ID: "null", Message: "unit testing", URL: "null", Active: false}}
}

View File

@ -113,7 +113,7 @@ func main() {
if ValidateSecurityWarnings { if ValidateSecurityWarnings {
isInitialized := make(chan bool) isInitialized := make(chan bool)
go v1alpha2.PluginsMgr.FetchPluginData(isInitialized) go v1alpha2.PluginsMgr.ManagePluginData(isInitialized)
if !<-isInitialized { if !<-isInitialized {
logger.Info("Unable to get the plugins data") logger.Info("Unable to get the plugins data")