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

Handle status monitor container reload #45

Merged
merged 2 commits into from
Dec 25, 2022
Merged

Conversation

dteplits
Copy link
Contributor

@dteplits dteplits commented Dec 8, 2022

Flow description:

  1. When status monitor container is loaded, it creates a zone file and initializes it with the header and A records. In this case SOA serial initial value will be 1
  2. Each time status monitor updates zone file, it increments SOA serial value
  3. Auto plugin inside Corefile is responsible to reload zone file newest data into the DNS when it identifies that SOA serial value was changed

PR adds the below logic:

If status monitor container was reloaded, on its startup it should check whether zone file already exist.
If yes, it should continue the existing SOA serial sequence instead of setting it back to 1.
The reason for that is the below edge case:

  • If zone file already exist and holds SOA serial = 1 (for example) and status monitor container was restarted
  • Then even after status monitor will override zone file content with the new data, the SOA serial will remain the same (=1)
  • Thus it will not be reloaded by the auto plugin since the SOA serial didn't change

Release note:

NONE

@kubevirt-bot kubevirt-bot added release-note-none Denotes a PR that doesn't merit a release note. dco-signoff: yes Indicates the PR's author has DCO signed all their commits. labels Dec 8, 2022
Copy link
Collaborator

@oshoval oshoval left a comment

Choose a reason for hiding this comment

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

Thanks
partial review

Please consider updating PR desc with more precise flow that cause the dead end

@@ -55,10 +56,33 @@ func (zoneFileCache *ZoneFileCache) prepare() {
zoneFileCache.generateHeaderSuffix()
zoneFileCache.soaSerial = 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

please read the file here, and set the serial either 0 or whatever the file has if exists
then no need this part
https://github.com/kubevirt/kubesecondarydns/pull/45/files#diff-91aeb36dc70c24eda218702f6964e34e7cbb3c7f29cce4f45ed3b09b6f59cb11R44

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have each file responsible for a different kind of activity, ZoneFile is responsible for dealing with the disk zone file and ZoneFileCache is responsible for zone cache maintenance. And ZoneManager is responsible for the flow logic. I don't think mixing it is a good idea.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It is fine that there will be an helper function that reads the file in order to determine the SOA to start from.
It is better imo than to create the header twice

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines 69 to 85
func fetchSoaSerial(content []byte) int {
contentStr := string(content)
var ind1, ind2 int
if ind1 = strings.Index(contentStr, "("); ind1 == 0 {
return 0
}
if ind2 = strings.Index(contentStr[ind1:], " "); ind2 == 0 {
return 0
}
soaSerial := strings.TrimSpace(contentStr[ind1+1 : ind1+ind2])
if soaSerialInt, err := strconv.Atoi(soaSerial); err != nil {
return 0
} else {
return soaSerialInt
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

there might be a way using regex to fetch it, will be nice if we can check please

Copy link
Collaborator

Choose a reason for hiding this comment

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

Btw according https://www.ietf.org/rfc/rfc1035.txt you can add comments (start with ;)
if the first line will be a comment with the serial, you can read just that line instead the whole file and get the serial

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to using regex.
Regarding the "reading line instead of the whole file", I can add it in a different PR

@@ -40,6 +40,10 @@ func (zoneMgr *ZoneManager) prepare() {
zoneMgr.zoneFileCache = NewZoneFileCache(nameServerIP, domain)

zoneMgr.zoneFile = NewZoneFile(zoneFileName + zoneMgr.zoneFileCache.domain)

if content, _ := zoneMgr.zoneFile.readFile(); content != nil && len(content) > 0 {
Copy link
Collaborator

@oshoval oshoval Dec 8, 2022

Choose a reason for hiding this comment

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

please verify that error is nil as well
if it is !=nil it is allowed to only be "not found"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@dteplits dteplits force-pushed the dns_sec branch 2 times, most recently from d3ec9aa to 3348fc4 Compare December 8, 2022 14:23
@dteplits dteplits marked this pull request as draft December 8, 2022 15:32
@kubevirt-bot kubevirt-bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Dec 8, 2022
@dteplits dteplits force-pushed the dns_sec branch 2 times, most recently from ea7c401 to 75b2815 Compare December 8, 2022 15:57
@dteplits dteplits marked this pull request as ready for review December 8, 2022 16:06
@kubevirt-bot kubevirt-bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Dec 8, 2022
Comment on lines 73 to 85
func fetchSoaSerial(content []byte) int {
if result := soaSerialReg.Find(content); result != nil {
soaSerial := strings.TrimSpace(string(result)[1:])
if soaSerialInt, err := strconv.Atoi(soaSerial); err == nil {
return soaSerialInt
} else {
return 0
}
} else {
return 0
}
}

Copy link
Collaborator

@oshoval oshoval Dec 11, 2022

Choose a reason for hiding this comment

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

Consider please something like this

package main

import (
	"fmt"
	"regexp"
)

func main() {
	s := "/user/999/edit"
	re := regexp.MustCompile(`/user/([0-9]+)/edit$`)
	x := re.FindStringSubmatch(s)
	fmt.Println(x[1])
}

Prints the 999

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm doing pretty match the same:
your example:
re := regexp.MustCompile(/user/([0-9]+)/edit$)
x := re.FindStringSubmatch(s)
my code:
soaSerialReg = regexp.MustCompile("\([0-9]+ ")
result := soaSerialReg.Find(content)

In this case regexp.Find does the job, I'm not sure why we need to use FindStringSubmatch() function.
The rest of the code removes unnecessary white spaces (if any) and converts string into a numeric.
So I'm not sure what exactly can/should be improved here.

Copy link
Collaborator

@oshoval oshoval Dec 14, 2022

Choose a reason for hiding this comment

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

Its saves usage of TrimSpace and Atoi, we don't need to reinvent please, when higher level exists.
you can give a regex, and it will do everything else for you, giving you the chunk by its own
just need to make sure please you still handle errors as well if the file is corrupted / NA etc

EDIT - right the Atoi is not saved, but it use FindStringSubmatch instead of Find

Copy link
Member

@AlonaKaplan AlonaKaplan left a comment

Choose a reason for hiding this comment

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

General comment that I had to give on previous PRs (and can be fixed on a follow-up PR). I would change the code so there will be a clear API between ZoneFile, ZoneFileCache and Zonemanager.
I mean, treat them as if they are in separate packages and there is no access to prived methods and data members.
I believe it will make the code much more readable and safe.

@@ -19,3 +17,7 @@ func NewZoneFile(fileName string) *ZoneFile {
func (zoneFile *ZoneFile) writeFile(content string) (err error) {
return os.WriteFile(zoneFile.zoneFileFullName, []byte(content), zoneFilePerm)
}

func readFile(zoneFileName string) ([]byte, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Why isn't it a method of ZoneFile like writeFile?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because it's a "util" function, I use it inside ZoneFileCache in order to fetch SoaSerial.
In the previous version this method was part of ZoneFile and ZoneManager did the job since it holds ZoneFile instance.
But Or suggested to move this logic into ZoneFileCache file and use readFile as a helper function.
Here is the conversation:
#45 (comment)

I can create a util file for readFile function, I just wanted to spare creating another file just for one method that anyway matches ZoneFile functionality

Copy link
Collaborator

Choose a reason for hiding this comment

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

You can strive to make it as an API or so please (as Alona suggested #45 (review))

Copy link
Member

Choose a reason for hiding this comment

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

I prefer the previous design. I find it cleaner that the ZoneCache is not familiar with the ZoneFile.

Copy link
Collaborator

@oshoval oshoval Dec 18, 2022

Choose a reason for hiding this comment

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

ZoneFile is a general util / this is the purpose of it, so it is totally fine imo,
there should not be two places that determine the serial and rebuild the header
the flow was much harder to grasp
maybe we can fine tune to something that we all agree upon

Copy link
Member

Choose a reason for hiding this comment

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

Why not reading the SOA from the file via ZoneManager and passing it to the ZoneCache?

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is an option, this is not the previous design
as long as we set initial serial once and build the header once without rebuild in case a serial exists, it is good imo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem is with the zone file name. It is initiated based on the domain name. Which is set inside the ZoneCache. So in current design you can't read the SOA from the file and pass it to ZoneCache without first initializing the ZoneCache. Perhaps it can be changed by changing the design slightly, not sure it's worth it

Copy link
Collaborator

@oshoval oshoval Dec 18, 2022

Choose a reason for hiding this comment

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

You can find the only file in the folder, if exist by scanning the folder
if we all like the idea
then you don't need the name of it
if you want to bullet proof it, you can find the only file that starts with db in the folder etc

Imo the current status is better, or at least worth to think later about something but not as part of this PR
unless you insist

another option is to read ad hoc the ORIGIN in order to just know the zone file name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have a problem in general with approaching ZoneFile from ZoneFileCache as it breaks current design. I will soon push another PR with a suggestion of small design change to handle it better. Let's see if it solves the issue.

zoneFileCache.header = zoneFileCache.generateHeader()
zoneFileCache.content = zoneFileCache.header
Copy link
Member

Choose a reason for hiding this comment

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

Why was this line removed and he is it related to this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just noticed that it's unnecessary. Not related to this PR.

Copy link
Member

Choose a reason for hiding this comment

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

Now it's back?

Copy link
Member

Choose a reason for hiding this comment

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

Previously in this PR zoneFileCache.content = zoneFileCache.header was removed. Now it's back, why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed

@@ -23,6 +25,8 @@ const (
adminEmailDefault = "email"
)

var soaSerialReg = regexp.MustCompile("\\([0-9]+ ")
Copy link
Member

Choose a reason for hiding this comment

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

I would change to "SOA .*\(([0-9]+) " to be more explicit.

Expect(os.RemoveAll("zones")).To(Succeed())
})

It("should set SOA serial equal to existing value", func() {
Copy link
Member

Choose a reason for hiding this comment

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

I think the method the has to be tested is the prive one NewZoneFileCache and not an internal method.
I would call NewZoneFileCacheand check the header. Same as the other tests.

Copy link
Member

Choose a reason for hiding this comment

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

Doesn't seem this comment was handled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@dteplits
Copy link
Contributor Author

dteplits commented Dec 14, 2022

General comment that I had to give on previous PRs (and can be fixed on a follow-up PR). I would change the code so there will be a clear API between ZoneFile, ZoneFileCache and Zonemanager. I mean, treat them as if they are in separate packages and there is no access to prived methods and data members. I believe it will make the code much more readable and safe.

Methods that I consider as a public API are:
ZoneManager:

  • NewZoneManager()
  • UpdateZone()
    ZoneFileCache:
  • NewZoneFileCache()
  • updateVMIRecords()
    ZoneFile:
  • NewZoneFile()
  • writeFile()
  • readFile() - "static" function

If I want to treat them as they are in a separate packages, I should make them public.
It's not really necessary since it's still the same package but for the clarity of the code perhaps I should.
Regarding the unit tests, I will change it to use public methods only.

@oshoval
Copy link
Collaborator

oshoval commented Dec 14, 2022

Regarding the unit tests, I will change it to use public methods only.

Unit test can check private methods as well

@dteplits
Copy link
Contributor Author

Regarding the unit tests, I will change it to use public methods only.

Unit test can check private methods as well

Then I don't see a problem :)
The only private method I use outside its file is ZoneFileCache.initSoaSerial() - in unit test
Anyway, I can use a public one instead if necessary.

@oshoval
Copy link
Collaborator

oshoval commented Dec 14, 2022

Regarding the unit tests, I will change it to use public methods only.

Unit test can check private methods as well

Then I don't see a problem :) The only private method I use outside its file is ZoneFileCache.initSoaSerial() - in unit test Anyway, I can use a public one instead if necessary.

The problem that Alona raise is the decoupling between the components IIUC
unrelated to my specific comment about unit tests.
Connection between components is done by the APIs.

@dteplits
Copy link
Contributor Author

Regarding the unit tests, I will change it to use public methods only.

Unit test can check private methods as well

Then I don't see a problem :) The only private method I use outside its file is ZoneFileCache.initSoaSerial() - in unit test Anyway, I can use a public one instead if necessary.

The problem that Alona raise is the decoupling between the components IIUC unrelated to my specific comment about unit tests. Connection between components is done by the APIs.

So based on the list I specified earlier (public API methods), converting those methods to public should do the job?
This is an API as I define it. Do I miss something?

@AlonaKaplan
Copy link
Member

Regarding the unit tests, I will change it to use public methods only.

Unit test can check private methods as well

Then I don't see a problem :) The only private method I use outside its file is ZoneFileCache.initSoaSerial() - in unit test Anyway, I can use a public one instead if necessary.

The problem that Alona raise is the decoupling between the components IIUC unrelated to my specific comment about unit tests. Connection between components is done by the APIs.

So based on the list I specified earlier (public API methods), converting those methods to public should do the job? This is an API as I define it. Do I miss something?

Moving zone_file.go and zone_file_cache.go to package internal should block accessing their public methods outside of the package - https://dave.cheney.net/2019/10/06/use-internal-packages-to-reduce-your-public-api-surface

@oshoval
Copy link
Collaborator

oshoval commented Dec 15, 2022

Moving zone_file.go and zone_file_cache.go to package internal should block accessing their public methods outside of the package - https://dave.cheney.net/2019/10/06/use-internal-packages-to-reduce-your-public-api-surface

Imo we can just use Cap and non Cap
draw 3 boxes, one for each module, and just the functions that have cross talk declare as API and expose.
The internal can be the next step if needed, and then it might be easier because each module will also have its own folder
(as the internal is based on folder structure)

Comment on lines 74 to 82
if result := soaSerialReg.FindStringSubmatch(content); result != nil && len(result) > 0 {
soaSerial := result[1]
if soaSerialInt, err := strconv.Atoi(soaSerial); err == nil {
return soaSerialInt
} else {
return 0
}
} else {
return 0
}
Copy link
Collaborator

@oshoval oshoval Dec 18, 2022

Choose a reason for hiding this comment

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

Lets try please to reduce else and indent

serial := 0
if result := soaSerialReg.FindStringSubmatch(content); result != nil && len(result) > 0 {
    soaSerial := result[1]
    if soaSerialInt, err := strconv.Atoi(soaSerial); err == nil {
        serial = soaSerialInt // or even just return soaSerialInt, and then in the last line return 0 instead
    } 
}
return serial

same can be done for the function above please

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@dteplits
Copy link
Contributor Author

Regarding the unit tests, I will change it to use public methods only.

Unit test can check private methods as well

Then I don't see a problem :) The only private method I use outside its file is ZoneFileCache.initSoaSerial() - in unit test Anyway, I can use a public one instead if necessary.

The problem that Alona raise is the decoupling between the components IIUC unrelated to my specific comment about unit tests. Connection between components is done by the APIs.

So based on the list I specified earlier (public API methods), converting those methods to public should do the job? This is an API as I define it. Do I miss something?

Moving zone_file.go and zone_file_cache.go to package internal should block accessing their public methods outside of the package - https://dave.cheney.net/2019/10/06/use-internal-packages-to-reduce-your-public-api-surface

will be fixed in a separate PR


zoneMgr.zoneFile = NewZoneFile(zoneFileName + zoneMgr.zoneFileCache.domain)
func (zoneMgr *ZoneManager) initSoaSerial() int {
Copy link
Member

Choose a reason for hiding this comment

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

I think this method should be moved to ZoneFile. I would call it ReadSoaSerial. If the file doesn't exist it should return nil, otherwise, the value from the file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

return serial
}

func fetchSoaSerial(content string) int {
Copy link
Member

Choose a reason for hiding this comment

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

same

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

testFileFunc(zoneFileUpdatedContent)
})

It("should read SOA serial", func() {
Copy link
Member

Choose a reason for hiding this comment

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

Please add a test testing ReadSoaSerial to the scenario the file doesn't exist.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}

func (zoneFile *ZoneFile) ReadSoaSerial() int {
serial := 0
Copy link
Member

Choose a reason for hiding this comment

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

I would return nil in case the file doesn't exist. The logic to set 0 in cases there is no value in the existing file should reside in the ZoneCache.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Collaborator

@oshoval oshoval Dec 21, 2022

Choose a reason for hiding this comment

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

Imo it is not needed, we don't need to differentiate and have logic for both nil and 0
just 0 is enough
it makes the code less nice and less readable with all those *int, conversions and such
the name of the function is ReadSoaSerial its already high level, it should be allowed to return 0 as a serial

Copy link
Member

Choose a reason for hiding this comment

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

I don't agree. ReadSoaSerial should only read the value from the file, it shouldn't have any heuristic of calculating the value in case the file doesn't exist, this logic should reside in the ZoneCache like the rest of the logic.

Copy link
Collaborator

@oshoval oshoval Dec 22, 2022

Choose a reason for hiding this comment

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

It is like a default value

you can name the function with something like CalculateSoaSerial
and then it will be responsible to give either 0 or nil,
otherwise we are drifting with conversions and ptr that we can and should eliminate

i wont insist, but it is not correct imo, keep it simple is much better
maybe there is a middle way you can think of that will make it nicer and you will like ?

btw there is one solution for it for example:
instead getting the DOMAIN by the ZoneManager, get it whenever needed,
this way we might able to pass the result less between modules


func (zoneFile *ZoneFile) ReadSoaSerial() int {
serial := 0
if content, err := zoneFile.readFile(); err == nil && content != nil {
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't ignore errors. If the error is that the file doesn't exist, it is ok, and we should return nil soaSerial. But for any other error we should return an error and not proceed as if nothing happened.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -160,7 +159,7 @@ var _ = Describe("cached zone file content maintenance", func() {

When("interfaces records list contains multiple vmis", func() {
BeforeEach(func() {
zoneFileCache = NewZoneFileCache(nameServerIP, domain)
zoneFileCache = NewZoneFileCache(nameServerIP, domain, 0)
Copy link
Member

Choose a reason for hiding this comment

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

Please add a test with non zero value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

return &isExist, err
}

func (zoneFile *ZoneFile) ReadSoaSerial() (*int, error) {
Copy link
Member

Choose a reason for hiding this comment

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

import "k8s.io/apimachinery/pkg/api/errors"

content, err := zoneFile.readFile()
if err != nil && errors.IsNotFound(err) {
    return nil, nil
}

if err != nil || content == nil {
  return nil, err
}
return fetchSoaSerial(string(content))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure that os.ReadFile() returns a specific error in case of FileNotFound. The documentation does not specify it. I only found a recommendation to check it with os.Stat()

zoneFileCache.header = zoneFileCache.generateHeader()
zoneFileCache.content = zoneFileCache.header
Copy link
Member

Choose a reason for hiding this comment

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

Previously in this PR zoneFileCache.content = zoneFileCache.header was removed. Now it's back, why?

zoneMgr.zoneFileCache = NewZoneFileCache(nameServerIP, domain)

zoneMgr.zoneFile = NewZoneFile(zoneFileName + zoneMgr.zoneFileCache.domain)
if soaSerial, err := zoneMgr.zoneFile.ReadSoaSerial(); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

I find this more readable.

soaSerial, err := zoneMgr.zoneFile.ReadSoaSerial()
if err != nil {
  return err
}
zoneMgr.zoneFileCache = NewZoneFileCache(nameServerIP, domain, soaSerial)
return nil

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@@ -111,7 +117,7 @@ var _ = Describe("cached zone file content maintenance", func() {

When("interfaces records list contains single vmi", func() {
BeforeEach(func() {
zoneFileCache = NewZoneFileCache(nameServerIP, domain)
zoneFileCache = NewZoneFileCache(nameServerIP, domain, nil)
Copy link
Member

Choose a reason for hiding this comment

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

I would change at least one on the next tests to use non nil soa number.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add a new test for it

Copy link
Collaborator

@oshoval oshoval left a comment

Choose a reason for hiding this comment

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

Thanks

} else if errors.Is(err, os.ErrNotExist) {
err = nil
}
return &isExist, err
Copy link
Collaborator

Choose a reason for hiding this comment

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

why we need *bool and not just bool ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

if isFileExist, err := zoneFile.isFileExist(); !*isFileExist || err != nil {
return nil, err
}
if content, err := zoneFile.readFile(); content == nil || err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

worth to add please the logic of isFileExist to readFile
wdyt ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I considered it and preferred to separate them as 1. each function has a different responsibility 2. more readable and clear

Copy link
Collaborator

Choose a reason for hiding this comment

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

I understand, but on the other hand one can argue readFile should handle non existing file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in this case it returns content=nil, just like os.ReadFile()

Copy link
Collaborator

@oshoval oshoval Dec 21, 2022

Choose a reason for hiding this comment

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

if readFile would check before that isFileExist it will know to return content == nil if file doesn't exists, with err == nil, and different error in case it couldn't read the file contents

I won't insist on it, but imo it is nicer

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would leave it as it is if you don't mind, I find it more readable

}
}

func fetchSoaSerial(content string) (*int, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why we need *int not just int ?

please use uint32

soaSerialInt can be named soaSerial

Copy link
Contributor Author

Choose a reason for hiding this comment

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

since I have to be able to return nil

Copy link
Collaborator

Choose a reason for hiding this comment

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

just return 0, why we need nil ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -40,10 +39,16 @@ type ZoneFileCache struct {
vmiRecordsMap map[string][]string
}

func NewZoneFileCache(nameServerIP string, domain string) *ZoneFileCache {
func NewZoneFileCache(nameServerIP string, domain string, soaSerial *int) *ZoneFileCache {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why *int and not just int ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

since I want to support nil value

Copy link
Collaborator

Choose a reason for hiding this comment

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

just return 0, why we need nil ?
(same for all places)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -40,10 +39,16 @@ type ZoneFileCache struct {
vmiRecordsMap map[string][]string
}

func NewZoneFileCache(nameServerIP string, domain string) *ZoneFileCache {
func NewZoneFileCache(nameServerIP string, domain string, soaSerial *int) *ZoneFileCache {
soaSerialInt := 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

soaSerialInt better to just use the argument soaSerial as is, and it will have (does it already ? if not please check) the value zero if the fetch didnt find a previous serial

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the idea is to handle nil value if ZoneFile not exist

Copy link
Collaborator

Choose a reason for hiding this comment

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

but 0 is good enough, as long as there is no error, we don't need to differentiate between nil and 0

Copy link
Contributor Author

Choose a reason for hiding this comment

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

zoneMgr.zoneFile = NewZoneFile(zoneFileName + zoneMgr.zoneFileCache.domain)
if soaSerial, err := zoneMgr.zoneFile.ReadSoaSerial(); err != nil {
return err
} else {
Copy link
Collaborator

@oshoval oshoval Dec 21, 2022

Choose a reason for hiding this comment

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

the else can be dropped, please simply do the code in it, because if the "if" caught it had a return

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed following #45 (comment)

zoneMgr = NewZoneManager()
var err error
zoneMgr, err = NewZoneManager()
Expect(err).To(BeNil())
Copy link
Collaborator

Choose a reason for hiding this comment

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

ToNot(HaveOccurred())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

It("should read SOA serial", func() {
Expect(os.WriteFile(zoneFileName, []byte(headerSoaSerial), 0777)).To(Succeed())
soaSerial, err := zoneFile.ReadSoaSerial()
Expect(err).To(BeNil())
Copy link
Collaborator

Choose a reason for hiding this comment

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

ToNot(HaveOccurred())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

When zone manager is created, if zone file already exist and contains SOA serial,
then the next SOA serial should continue the existing one.

Signed-off-by: Diana Teplits <dteplits@redhat.com>
@AlonaKaplan
Copy link
Member

/approve

Copy link
Collaborator

@oshoval oshoval left a comment

Choose a reason for hiding this comment

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

Thanks

@kubevirt-bot kubevirt-bot added the lgtm Indicates that a PR is ready to be merged. label Dec 25, 2022

It("should return nil when try to read SOA serial", func() {
soaSerial, err := zoneFile.ReadSoaSerial()
Expect(err).To(BeNil())
Copy link
Collaborator

@oshoval oshoval Dec 25, 2022

Choose a reason for hiding this comment

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

Please consider fixing this one on either this PR or a follow one
Expect(err).ToNot(HaveOccurred())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Signed-off-by: Diana Teplits <dteplits@redhat.com>
@kubevirt-bot kubevirt-bot removed the lgtm Indicates that a PR is ready to be merged. label Dec 25, 2022
@kubevirt-bot kubevirt-bot added the lgtm Indicates that a PR is ready to be merged. label Dec 25, 2022
@AlonaKaplan
Copy link
Member

/approve

@kubevirt-bot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: AlonaKaplan

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubevirt-bot kubevirt-bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Dec 25, 2022
@kubevirt-bot kubevirt-bot merged commit d73a470 into kubevirt:main Dec 25, 2022
@oshoval
Copy link
Collaborator

oshoval commented Jan 29, 2023

I think we didn't cover the following case:
Status-monitor container crashed, for example if someone removed kubevirt for more than 2 minutes
and then deleted all the VMs
Now reinstall kubevirt, and the container is starting with the previous SOA, but it doesnt write the file
as long as there is no update, and the zone file still have the old VMs
the safest solution is to rewrite the file (with serial++) without vms if the soa was there (but with the header and such, so we wont lose the serial in case of another restart)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. dco-signoff: yes Indicates the PR's author has DCO signed all their commits. lgtm Indicates that a PR is ready to be merged. release-note-none Denotes a PR that doesn't merit a release note. size/L
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants