diff --git a/cnf-certification-test/webserver/index.html b/cnf-certification-test/webserver/index.html index 07611612a..d878b7dc0 100644 --- a/cnf-certification-test/webserver/index.html +++ b/cnf-certification-test/webserver/index.html @@ -1,5 +1,6 @@ + @@ -9,6 +10,9 @@ sizes="any" href="https://ux.redhat.com/assets/logo-red-hat.svg"> + + + + - + +
Red Hat

CNF Certification Test

-
@@ -165,19 +195,81 @@

CNF Certification Test

required type="file"> - -
+ Upload TNF Configuration File: + +
+
TNF Configuration - - - + Add NameSpace + Remove NameSpace +
- + Add podsUnderTestLabels
+ Remove podsUnderTestLabels + +
+ + Add operatorsUnderTestLabels
+ Remove operatorsUnderTestLabels + + + Add targetCrdFilters
+ Remove targetCrdFilters + + + Add managedDeployments
+ Remove managedDeployments + + + + Add managedStatefulsets
+ Remove managedStatefulsets + + + Add acceptedKernelTaints
+ Remove acceptedKernelTaints + + + Add skipHelmChartList
+ Remove skipHelmChartList + + + + Add skipScalingTestDeployments
+ Remove skipScalingTestDeployments + + + Add skipScalingTestStatefulsets
+ Remove skipScalingTestStatefulsets + + + Add servicesignorelist
+ Remove servicesignorelist + + + + + + + + + + + + Add ValidProtocolNames
+ Remove ValidProtocolNames + + + +
+
Select a Test @@ -185,17 +277,36 @@

CNF Certification Test

Lifecycle + Manageability + +
Run Certification Test diff --git a/cnf-certification-test/webserver/index.js b/cnf-certification-test/webserver/index.js new file mode 100644 index 000000000..2d361c8df --- /dev/null +++ b/cnf-certification-test/webserver/index.js @@ -0,0 +1,214 @@ +let yamlGlobal +var counters = {}; + +function addCrdElem(counters,name1,name2,clickedId){ + + console.log(clickedId) + + if (!counters[clickedId]|| counters[clickedId] <= 1 ) { + counters[clickedId] = 1; + var i= counters[clickedId] + var t1=clickedId+name1+i+'' + var t2=clickedId+name2+i+'' + var field = + ''+name1+':' + + ''+name2+':' ; + var target=clickedId+'add' + $('#'+target).after(field); + } + else{ + var i= counters[clickedId] + var t1=clickedId+name1+i+'' + var t2=clickedId+name2+i+'' + var field = + ''+name1+':' + + ''+name2+':' ; + var target=clickedId+name2+(i-1)+'' + $('#'+target).after(field); + } + return counters + } + + + $(document).ready(function() { + document.getElementById('targetNameSpacesremove').style.display = 'none'; + document.getElementById('podsUnderTestLabelsremove').style.display = 'none'; + document.getElementById('operatorsUnderTestLabelsremove').style.display = 'none'; + document.getElementById('targetCrdFiltersremove').style.display = 'none'; + document.getElementById('managedDeploymentsremove').style.display = 'none'; + document.getElementById('managedStatefulsetsremove').style.display = 'none'; + document.getElementById('acceptedKernelTaintsremove').style.display = 'none'; + document.getElementById('skipHelmChartListremove').style.display = 'none'; + document.getElementById('servicesignorelistremove').style.display = 'none'; + document.getElementById('skipScalingTestDeploymentsremove').style.display = 'none'; + document.getElementById('skipScalingTestStatefulsetsremove').style.display = 'none'; + document.getElementById('ValidProtocolNamesremove').style.display = 'none'; + const inputElement = document.getElementById('tnfFile') + inputElement.addEventListener('change', handleTnfFiles, false) + $('.add').on('click', function() { + var clickedIdOrg = $(this).attr('id'); + var clickedId = clickedIdOrg.replace('add', ''); + if (clickedId == 'targetCrdFilters'){ + counters=addCrdElem(counters,'nameSuffix','scalable',clickedId) + } + else if (clickedId == 'skipScalingTestDeployments'){ + counters=addCrdElem(counters,'name','namespace',clickedId) + } + else if (clickedId == 'skipScalingTestStatefulsets'){ + counters=addCrdElem(counters,'name','namespace',clickedId) + } + else{ + if (!counters[clickedId]|| counters[clickedId] <= 1 ) { + counters[clickedId] = 1; + var i= counters[clickedId] + console.log(i) + console.log(clickedId) + var field = ''+ + ''; + $('#'+clickedIdOrg).after(field); + } + else{ + var i= counters[clickedId] + var field = ''+ + ''; + var target=clickedId+(i-1)+'' + $('#'+target).after(field); + } + } + document.getElementById(clickedId+'remove').style.display = 'block'; + counters[clickedId] = counters[clickedId] + 1; // Increment the counter for the next call + }) + // Remove last text input + $('.remove').on('click', function() { + var clickedIdOrg = $(this).attr('id'); + var clickedId = clickedIdOrg.replace('remove', ''); + var i= counters[clickedId] + if (i>1){ + var target=clickedId+(i-1)+'' + if (clickedId == 'targetCrdFilters'){ + var target1='targetCrdFiltersnameSuffix'+(i-1)+'' + var target2='targetCrdFiltersscalable'+(i-1)+'' + console.log(target2) + $('#'+target1).remove(); + $('h7[for="' + target1 + '"]').remove(); + $('#'+target2).remove(); + $('h7[for="' + target2 + '"]').remove(); + }if (clickedId == 'skipScalingTestDeployments'){ + var target1='skipScalingTestDeploymentsname'+(i-1)+'' + var target2='skipScalingTestDeploymentsnamespace'+(i-1)+'' + console.log(target2) + $('#'+target1).remove(); + $('h7[for="' + target1 + '"]').remove(); + $('#'+target2).remove(); + $('h7[for="' + target2 + '"]').remove(); + }if (clickedId == 'skipScalingTestStatefulsets'){ + var target1='skipScalingTestStatefulsetsname'+(i-1)+'' + var target2='skipScalingTestStatefulsetsnamespace'+(i-1)+'' + console.log(target2) + $('#'+target1).remove(); + $('h7[for="' + target1 + '"]').remove(); + $('#'+target2).remove(); + $('h7[for="' + target2 + '"]').remove(); + } + else{ + $('#'+target).remove(); + } + $('label[for="' + target + '"]').remove(); + counters[clickedId] = counters[clickedId] - 1; + if (i-1 ==1){ + document.getElementById(clickedIdOrg).style.display = 'none'; + } + } + }); + }) + + function handleTnfFiles () { + const fileList = this.files + if (fileList.length) { + // We have a file to load + const fileUploaded = new FileReader() + fileUploaded.addEventListener('load', e => { + yamlGlobal = jsyaml.load(fileUploaded.result); + renderResults() + }) + fileUploaded.readAsText(fileList[0]) + } + } + + // render results tab +function renderResults () { +if (typeof yamlGlobal !== 'undefined') { +fillData(yamlGlobal.targetNameSpaces, '#targetNameSpacesadd','targetNameSpaces','name','') +fillData(yamlGlobal.managedDeployments, '#managedDeploymentsadd','managedDeployments','name','') +fillData(yamlGlobal.managedStatefulsets, '#managedStatefulsetsadd','managedStatefulsets','name','') +fillData(yamlGlobal.acceptedKernelTaints, '#acceptedKernelTaintsadd','acceptedKernelTaints','module','') +fillData(yamlGlobal.skipHelmChartList, '#skipHelmChartListadd','skipHelmChartList','name','') + +fillData(yamlGlobal.podsUnderTestLabels, '#podsUnderTestLabelsadd','podsUnderTestLabels','','') +fillData(yamlGlobal.servicesignorelist, '#servicesignorelistadd','servicesignorelist','','') +fillData(yamlGlobal.validProtocolNames, '#ValidProtocolNamesadd','ValidProtocolNames','','') +fillData(yamlGlobal.operatorsUnderTestLabels, '#operatorsUnderTestLabelsadd','operatorsUnderTestLabels','','') + + +fillData(yamlGlobal.skipScalingTestDeployments, '#skipScalingTestDeploymentsadd','skipScalingTestDeployments','name','namespace') +fillData(yamlGlobal.skipScalingTestStatefulsets, '#skipScalingTestStatefulsetsadd','skipScalingTestStatefulsets','name','namespace') +fillData(yamlGlobal.targetCrdFilters, '#targetCrdFiltersadd','targetCrdFilters','nameSuffix','scalable') +if (yamlGlobal.DebugDaemonSetNamespace){ +document.getElementById('DebugDaemonSetNamespace').value = yamlGlobal.DebugDaemonSetNamespace; +} +if (yamlGlobal.DebugDaemonSetNamespace){ + document.getElementById('CollectorAppEndPoint').value = yamlGlobal.collectorAppEndPoint; +} +if(yamlGlobal.executedBy){ + document.getElementById('executedBy').value = yamlGlobal.executedBy; +} +if(yamlGlobal.partnerName){ + document.getElementById('PartnerName').value = yamlGlobal.partnerName; +} + + +} +} +function fillData(input,element,clickedId,keyname,key2name){ +if (!counters[clickedId]){ +counters[clickedId] =1 +} +for (const key in input) { +var target=clickedId+(counters[clickedId]-1)+'' +if (keyname!=''){ + var value = input[key][keyname] +}else{ + var value = input[key] +} +if(key2name==''){ + console.log('heeeer') + + var pf_txt='' +}else{ + console.log(input[key][keyname]) + console.log(input[key][key2name]) + var i= counters[clickedId] + var t1=clickedId+keyname+i+'' + var t2=clickedId+key2name+i+'' + console.log(t1) + var pf_txt = + ''+keyname+':' + + ''+key2name+':' ; + var target=clickedId+key2name+(i-1)+'' +} +var field=''+pf_txt + if ( counters[clickedId]==1){ + $(element).after(field); + }else{ + $('#'+target).after(field); + } + counters[clickedId]= counters[clickedId]+1 +} +} \ No newline at end of file diff --git a/cnf-certification-test/webserver/submit.js b/cnf-certification-test/webserver/submit.js index e06d0aab9..3834401d4 100644 --- a/cnf-certification-test/webserver/submit.js +++ b/cnf-certification-test/webserver/submit.js @@ -2,15 +2,40 @@ export async function submit(form) { form.elements.submit.disabled = true; const formdata = new FormData(form); + // Iterate over form elements and add those with non-empty values to FormData + Array.from(form.elements).forEach(element => { + if ( element.hasAttribute('value') && element.type!="checkbox") { + console.log(element) + console.log(element.value) + console.log((element.id.match(/[a-zA-Z]/g) || []).join('')) + formdata.append((element.id.match(/[a-zA-Z]/g) || []).join(''), element.value); + } + }); + console.log(JSON.stringify(Object.fromEntries(formdata))) for (const el of form.elements) if (el instanceof HTMLFieldSetElement) el.disabled = true - + console.log(Array.from(formdata.entries())); + // Collect data from form fields - const fields = Array.from(formdata.entries()).reduce((acc, [key, val]) => ({ ...acc, - [key]: key in acc ? [acc[key], val] : val - }), {}); + const fields = Array.from(formdata.entries()).reduce((acc, [key, val]) => { + console.log(acc[key]); + console.log(val); + + if (acc[key] === undefined) { + // If the key is not in the accumulator, set it to the value or an array with the value + acc[key] = [val]; + } else if (Array.isArray(acc[key])) { + // If the key is already an array, push the new value to the array + acc[key].push(val); + } else { + // If the key is a single value, convert it to an array with both values + acc[key] = [acc[key], val]; + } + return acc; + }, {}); delete fields.submit; console.log(fields); + console.log(formdata) formdata.append("jsonData", JSON.stringify(fields)); // Send an HTTP request to the server to run the function diff --git a/cnf-certification-test/webserver/webserver.go b/cnf-certification-test/webserver/webserver.go index 591b478e1..b575d0508 100644 --- a/cnf-certification-test/webserver/webserver.go +++ b/cnf-certification-test/webserver/webserver.go @@ -20,7 +20,9 @@ import ( "github.com/sirupsen/logrus" "github.com/test-network-function/cnf-certification-test/internal/clientsholder" "github.com/test-network-function/cnf-certification-test/pkg/certsuite" + "github.com/test-network-function/cnf-certification-test/pkg/configuration" "github.com/test-network-function/cnf-certification-test/pkg/provider" + yaml "gopkg.in/yaml.v2" ) type webServerContextKey string @@ -44,6 +46,10 @@ var logs []byte //go:embed toast.js var toast []byte + +//go:embed index.js +var index []byte + var Buf *bytes.Buffer var upgrader = websocket.Upgrader{ @@ -81,31 +87,32 @@ func logStreamHandler(w http.ResponseWriter, r *http.Request) { } type RequestedData struct { - SelectedOptions interface{} `json:"selectedOptions"` + SelectedOptions []string `json:"selectedOptions"` + TargetNameSpaces []string `json:"targetNameSpaces"` + PodsUnderTestLabels []string `json:"podsUnderTestLabels"` + OperatorsUnderTestLabels []string `json:"operatorsUnderTestLabels"` + ManagedDeployments []string `json:"managedDeployments"` + ManagedStatefulsets []string `json:"managedStatefulsets"` + SkipScalingTestDeploymentsnamespace []string `json:"skipScalingTestDeploymentsnamespace"` + SkipScalingTestDeploymentsname []string `json:"skipScalingTestDeploymentsname"` + SkipScalingTestStatefulsetsnamespace []string `json:"skipScalingTestStatefulsetsnamespace"` + SkipScalingTestStatefulsetsname []string `json:"skipScalingTestStatefulsetsname"` + TargetCrdFiltersnameSuffix []string `json:"targetCrdFiltersnameSuffix"` + TargetCrdFiltersscalable []string `json:"targetCrdFiltersscalable"` + AcceptedKernelTaints []string `json:"acceptedKernelTaints"` + SkipHelmChartList []string `json:"skipHelmChartList"` + Servicesignorelist []string `json:"servicesignorelist"` + ValidProtocolNames []string `json:"ValidProtocolNames"` + DebugDaemonSetNamespace []string `json:"DebugDaemonSetNamespace"` + CollectorAppEndPoint []string `json:"CollectorAppEndPoint"` + ExecutedBy []string `json:"executedBy"` + CollectorAppPassword []string `json:"CollectorAppPassword"` + PartnerName []string `json:"PartnerName"` } type ResponseData struct { Message string `json:"message"` } -func FlattenData(data interface{}, result []string) []string { - switch v := data.(type) { - case string: - result = append(result, v) - case []interface{}: - for _, item := range v { - result = FlattenData(item, result) - } - case map[string]interface{}: - for key, item := range v { - if key == "selectedOptions" { - result = FlattenData(item, result) - } - result = FlattenData(item, result) - } - } - return result -} - func installReqHandlers() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { // Set the content type to "text/html". @@ -150,6 +157,18 @@ func installReqHandlers() { return } }) + + http.HandleFunc("/index.js", func(w http.ResponseWriter, r *http.Request) { + // Set the content type to "application/javascript". + w.Header().Set("Content-Type", "application/javascript") + // Write the embedded JavaScript content to the response. + _, err := w.Write(index) + if err != nil { + http.Error(w, "Failed to write response", http.StatusInternalServerError) + return + } + }) + // Serve the static HTML file http.HandleFunc("/logstream", logStreamHandler) } @@ -157,10 +176,8 @@ func installReqHandlers() { func StartServer(outputFolder string) { ctx := context.Background() server := &http.Server{ - Addr: ":8084", // Server address - ReadTimeout: 10 * time.Second, // Maximum duration for reading the entire request - WriteTimeout: 10 * time.Second, // Maximum duration for writing the entire response - IdleTimeout: 120 * time.Second, // Maximum idle duration before closing the connection + Addr: ":8084", // Server address + ReadTimeout: 10 * time.Second, // Maximum duration for reading the entire request BaseContext: func(l net.Listener) context.Context { ctx = context.WithValue(ctx, outputFolderCtxKey, outputFolder) return ctx @@ -191,8 +208,7 @@ func runHandler(w http.ResponseWriter, r *http.Request) { if err := json.Unmarshal([]byte(jsonData), &data); err != nil { fmt.Println("Error:", err) } - var flattenedOptions []string - flattenedOptions = FlattenData(data.SelectedOptions, flattenedOptions) + flattenedOptions := data.SelectedOptions // Get the file from the request file, fileHeader, err := r.FormFile("kubeConfigPath") // "fileInput" is the name of the file input field @@ -228,6 +244,18 @@ func runHandler(w http.ResponseWriter, r *http.Request) { logrus.Infof("Web Server kubeconfig file : %v (copied into %v)", fileHeader.Filename, kubeconfigTempFile.Name()) logrus.Infof("Web Server Labels filter : %v", flattenedOptions) + tnfConfig, err := os.ReadFile("tnf_config.yml") + if err != nil { + logrus.Fatalf("Error reading YAML file: %v", err) + } + + newData := updateTnf(tnfConfig, &data) + + // Write the modified YAML data back to the file + err = os.WriteFile("tnf_config.yml", newData, os.ModePerm) + if err != nil { + logrus.Fatalf("Error writing YAML file: %v", err) + } _ = clientsholder.GetNewClientsHolder(kubeconfigTempFile.Name()) var env provider.TestEnvironment @@ -265,3 +293,99 @@ func runHandler(w http.ResponseWriter, r *http.Request) { return } } + +//nolint:funlen +func updateTnf(tnfConfig []byte, data *RequestedData) []byte { + // Unmarshal the YAML data into a Config struct + var config configuration.TestConfiguration + + err := yaml.Unmarshal(tnfConfig, &config) + if err != nil { + logrus.Fatalf("Error unmarshalling YAML: %v", err) + } + + // Modify the configuration + var namespace []configuration.Namespace + for _, tnamespace := range data.TargetNameSpaces { + namespace = append(namespace, configuration.Namespace{Name: tnamespace}) + } + config.TargetNameSpaces = namespace + + config.PodsUnderTestLabels = data.PodsUnderTestLabels + + config.OperatorsUnderTestLabels = data.OperatorsUnderTestLabels + + var managedDeployments []configuration.ManagedDeploymentsStatefulsets + for _, val := range data.ManagedDeployments { + managedDeployments = append(managedDeployments, configuration.ManagedDeploymentsStatefulsets{Name: val}) + } + config.ManagedDeployments = managedDeployments + + var managedStatefulsets []configuration.ManagedDeploymentsStatefulsets + for _, val := range data.ManagedDeployments { + managedStatefulsets = append(managedStatefulsets, configuration.ManagedDeploymentsStatefulsets{Name: val}) + } + config.ManagedStatefulsets = managedStatefulsets + + var crdFilter []configuration.CrdFilter + for i := range data.TargetCrdFiltersnameSuffix { + val := true + if data.TargetCrdFiltersscalable[i] == "false" { + val = false + } + crdFilter = append(crdFilter, configuration.CrdFilter{NameSuffix: data.TargetCrdFiltersnameSuffix[i], + Scalable: val}) + } + config.CrdFilters = crdFilter + + var acceptedKernelTaints []configuration.AcceptedKernelTaintsInfo + for _, val := range data.AcceptedKernelTaints { + acceptedKernelTaints = append(acceptedKernelTaints, configuration.AcceptedKernelTaintsInfo{Module: val}) + } + config.AcceptedKernelTaints = acceptedKernelTaints + + var skipHelmChartList []configuration.SkipHelmChartList + for _, val := range data.SkipHelmChartList { + skipHelmChartList = append(skipHelmChartList, configuration.SkipHelmChartList{Name: val}) + } + config.SkipHelmChartList = skipHelmChartList + + var skipScalingTestDeployments []configuration.SkipScalingTestDeploymentsInfo + for i := range data.SkipScalingTestDeploymentsname { + skipScalingTestDeployments = append(skipScalingTestDeployments, configuration.SkipScalingTestDeploymentsInfo{Name: data.SkipScalingTestDeploymentsname[i], + Namespace: data.SkipScalingTestDeploymentsnamespace[i]}) + } + config.SkipScalingTestDeployments = skipScalingTestDeployments + + var skipScalingTestStatefulSets []configuration.SkipScalingTestStatefulSetsInfo + for i := range data.SkipScalingTestStatefulsetsname { + skipScalingTestStatefulSets = append(skipScalingTestStatefulSets, configuration.SkipScalingTestStatefulSetsInfo{Name: data.SkipScalingTestStatefulsetsname[i], + Namespace: data.SkipScalingTestStatefulsetsnamespace[i]}) + } + config.SkipScalingTestStatefulSets = skipScalingTestStatefulSets + + config.ServicesIgnoreList = data.Servicesignorelist + config.ValidProtocolNames = data.ValidProtocolNames + if len(data.CollectorAppEndPoint) > 0 { + config.CollectorAppEndPoint = data.CollectorAppEndPoint[0] + } + if len(data.CollectorAppPassword) > 0 { + config.CollectorAppPassword = data.CollectorAppPassword[0] + } + if len(data.ExecutedBy) > 0 { + config.ExecutedBy = data.ExecutedBy[0] + } + if len(data.PartnerName) > 0 { + config.PartnerName = data.PartnerName[0] + } + if len(data.DebugDaemonSetNamespace) > 0 { + config.DebugDaemonSetNamespace = data.DebugDaemonSetNamespace[0] + } + + // Serialize the modified config back to YAML format + newData, err := yaml.Marshal(&config) + if err != nil { + logrus.Fatalf("Error marshaling YAML: %v", err) + } + return newData +}